# [algorithm] recursion, search and sorting

Posted by gareh on Tue, 22 Feb 2022 17:43:36 +0100

# 1. Recursion

1. Find duplicate

• Find a way to divide
• Find recursive formula or equivalent transformation
2. Find change

The variable is usually used as a parameter

3. Find boundary

## Simple basic questions (practice three steps)

• Find the factorial of n

```public class demo01 {
public static void main(String[] args) {
int a = f1(4);
System.out.println(a);
}

// Calculate factorial value
public static int f1(int n) {
if(n == 1)
return 1;
return n*f1(n-1);
}
}
```
• Print i to j

```public class demo02 {
public static void main(String[] args) {
f2(4,8);
}

// Print out i to j
public static void f2(int i, int j) {
if(i > j)
return;
System.out.print(i+" ");
f2(i+1,j);
}
}
```
• Sum all elements of arr

```public class demo03 {
public static void main(String[] args) {
int[] arr = new int[]{1, 2, 3, 4, 5};
int res = f3(arr, 0);
System.out.println(res);
}

// Sum all elements of arr
public static int f3(int[] arr, int begin) {
if(begin == arr.length-1)
return arr[begin];
return arr[begin] + f3(arr, begin+1);
}
}
```
• Flip string

```public class demo04 {
public static void main(String[] args) {
String s = "a54s5d";
System.out.println(f4(s, s.length()-1));
}

// Flip string
public static String f4(String s, int end) {
if (end == 0) {
return "" + s.charAt(0);
}
return s.charAt(end)+f4(s, end-1);
}
}
```

## Basic questions (change in repetition, repetition in change)

Recursion:

The above example is the direct quantity + small gauge mold problem

The following examples are multiple small gauge mold problems

### ● Fibonacci series

Find the recurrence formula f(n) = f(n-1) + f(n-2)

```public class demo05 {
// test
public static void main(String[] args) {
System.out.println(fib(3));
}

// Fibonacci sequence
public static int fib(int n) {
if(n == 1 || n == 2)
return 1;
return fib(n-1) + fib(n-2);	// Recursive tree, when n is large, the scale is large
}
}
```

### ● maximum common divisor

Rolling division method m% n = k

If k = 0, then n is the greatest common divisor

If K ≠ 0, then n% k = k ` (repeat this until the remainder is 0)

```public class demo03 {
// test
public static void main(String[] args) {
System.out.println(gcd(15, 9));
}

// greatest common divisor
public static int gcd(int m, int n) {
if (n == 0) {
return m;
}
return gcd(n, m % n);
}
}
```

### ● insert sort recursively

Insert sort is to treat the array as an ordered table and an unordered table. One number is taken out of the unordered table each time and sorted in the ordered table.

```public class demo04 {
public static void main(String[] args) {
int[] arr = new int[]{5,8,3,4,1};
insertSort(arr, arr.length-1);
for (int i : arr) {
System.out.print(i+" ");
}
}

public static void insertSort(int[] arr, int k) {
if(k == 0)
return;

// Sort the first k-1 elements
insertSort(arr, k-1);

// Insert the element of position k into the previous part
int x = arr[k];
int index = k - 1;
while(index > -1 && x < arr[index]) {
arr[index+1] = arr[index--];
}
arr[index+1] = x;
}
}
```

### ● tower of Hanoi 🔺

1 ~ N move from A to B, C as auxiliary

Equivalent to:

• Move 1 ~ N-1 from A to C and B as auxiliary
• Move N from A to B
• Move 1 ~ N-1 from C to B, A as auxiliary
```// Hanoi Tower recursive solution

public class demo05 {
public static void main(String[] args) {
HanoiTower(3, "A", "B", "C");
}

/**
* Print the path of moving N plates from source to target
* @param N       The initial n plates arrive from childhood, and N is the maximum number
* @param from 	Original column
* @param to 		Target column
* @param help    Auxiliary column
*/
public static void HanoiTower(int N, String from, String to, String help){
if(N == 1) {
System.out.println("move " + N + " from " + from + " to " + to);
return;
}

// Move 1 ~ N-1 to the auxiliary column
HanoiTower(N - 1, from, help, to);
// N successfully reached target
System.out.println("move " + N + " from " + from + " to " + to);
// Move 1 ~ N-1 from the auxiliary column to the target column
HanoiTower(N - 1, help, to, from);
}
}
```

# 2. Find

## Binary search

```public class binarySearch {
public static int BinarySearch(int[] arr, int low, int high, int target) {
if(low > high)
return -1;
int mid = low + ((high- low) >> 1);
//		int mid = (low + high) >>> 1; 	 Prevent overflow and shift efficiently
if(arr[mid] > target)
return BinarySearch(arr, low, mid-1, target);
else if(arr[mid] < target)
return BinarySearch(arr, mid+1, high, target);
else
return mid;
}

// test
public static void main(String[] args) {
int[] arr = new int[] {1,5,8,10,13,21,55};
System.out.println(BinarySearch(arr, 0, arr.length-1, 13));
}
}
```

# 3. Sorting

## Bubble sorting

• Time complexity: O(n) ²)
• Space complexity: O(1)
• Stability: the relative position of the same element will not change before and after sorting, that is, it is stable
```import java.util.Arrays;

public class bubbleSort {
public static void BubbleSort1(int[] arr) {
if (arr == null || arr.length <= 1) {
return;
}

for(int i = 0; i < arr.length - 1; i++) {	// Number of cycles, i.e. bubbling in the i-th cycle
for(int j = 0; j < arr.length - 1 - i; j++) {	// Pairwise comparison sort
if(arr[j] > arr[j + 1]) {
int temp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = temp;
}
}
}
}

// improvement
public static void BubbleSort2(int[] arr) {
for(int i = 0; i < arr.length - 1; i++) {
// Mark whether there is exchange on this trip. If there is no exchange, the order has been arranged
// At the beginning of each round, it is assumed that the order has been arranged
boolean flag = true;
for(int j = 0; j < arr.length - 1 - i; j++) {
if(arr[j] > arr[j + 1]) {
int temp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = temp;
flag = false;	// An exchange indicates that this round is not in order
}
}

// If there is no exchange, the order is already arranged, and there is no need to carry out the next round of bubbling
if(flag) {
break;
}
}
}

// test
public static void main(String[] args) {
int[] array1 = new int[]{55, 33, 22, 66, 11};
System.out.print("Before sorting:");
System.out.println(Arrays.toString(array1));

// Call the BubbleSort1 method to sort the array
BubbleSort1(array1);
System.out.print("BubbleSort1 After sorting:");
System.out.println(Arrays.toString(array1));

// Call the improved BubbleSort2 method to sort the array
int[] array2 = new int[]{55, 33, 22, 66, 11};
BubbleSort2(array2);
System.out.print("BubbleSort2 After sorting:");
System.out.println(Arrays.toString(array2));

// Array.sort()
int[] array3 = new int[]{55, 33, 22, 66, 11};
Arrays.sort(array3);
System.out.print("Arrays.sort After sorting:");
System.out.println(Arrays.toString(array3));

// You can use long now = system currentTimeMillis();
}
}
```

## Select sort

• Time complexity: O(n) ²)
• Space complexity: O(1)
• Stability: unstable
```import java.util.Arrays;

public class selectionSort {
// Select sort
public static void SelectionSort(int[] arr) {
if (arr == null || arr.length <= 1) {
return;
}

// Select the smallest (or largest) element from the data elements to be sorted in each trip as the first element until all elements are arranged
for(int i = 0; i < arr.length - 1; i++) {
int min = i;
for(int j = i + 1; j < arr.length; j++) {
if(arr[min] > arr[j])
min = j;
}
if(min != i)
swap(arr, i, min);
}
}

// exchange
public static void swap(int[] arr, int a, int b) {
int temp = arr[a];
arr[a] = arr[b];
arr[b] = temp;
}

// test
public static void main(String[] args) {
int[] arr = {4, 5, 6, 3, 2, 1};
System.out.print("Before sorting:");
System.out.println(Arrays.toString(arr));
SelectionSort(arr);
System.out.print("SelectionSort After sorting:");
System.out.println(Arrays.toString(arr));

int[] arr2 = {4, 5, 6, 3, 2, 1};
Arrays.sort(arr2);
System.out.print("Arrays.sort After sorting:");
System.out.println(Arrays.toString(arr2));
}
}
```

## Insert sort

• Time complexity: O(n) ²)
• Space complexity: O(1)
• Stability: stable
```import java.util.Arrays;

public class insertSort {
// Insert sort
public static void InsertSort(int[] arr) {
if (arr == null || arr.length <= 1) {
return;
}

for(int i = 1; i < arr.length; i++) {	// The first element is ordered, starting with the second
int temp = arr[i];	// Number of to insert
int j = i - 1;		// Scan the sorted part forward for insertion
while(j >= 0 && arr[j] > temp) {
arr[j + 1] = arr[j];	// temp is smaller than arr[j]. Move arr[j] backward
j--;
}
arr[j + 1] = temp;
}
}

// test
public static void main(String[] args){
int[] arr = {51,2,35,64,55,14,34,28,9};

System.out.print("Before sorting:");
System.out.println(Arrays.toString(arr));
InsertSort(arr);
System.out.print("SelectionSort After sorting:");
System.out.println(Arrays.toString(arr));

int[] arr2 = {51,2,35,64,55,14,34,28,9};
Arrays.sort(arr2);
System.out.print("Arrays.sort After sorting:");
System.out.println(Arrays.toString(arr2));
}
}
```

## Shell Sort

• Time complexity: in O (nlogn) ～ o (n) ²) between
• Space complexity: O(1)
• Stability: unstable
```import java.util.Arrays;

public class shellSort {
// Shell Sort
public static void ShellSort(int[] arr) {
if(arr == null || arr.length <= 1)
return;

// Change of control increment of outer loop
for(int interval = arr.length/2; interval > 0; interval/=2) {
// The inner loop performs insertion sorting with an increment of interval
for(int i = interval; i < arr.length; i++) {
int temp = arr[i];
int idx = i - interval;
while(idx >= 0 && arr[idx] > temp) {
arr[idx + interval] = arr[idx];
idx -= interval;
}
arr[idx + interval] = temp;
}
}
}

// test
public static void main(String[] args){
int[] arr = {51,2,35,64,55,14,34,28,9};

System.out.print("Before sorting:");
System.out.println(Arrays.toString(arr));
ShellSort(arr);
System.out.print("SelectionSort After sorting:");
System.out.println(Arrays.toString(arr));

int[] arr2 = {51,2,35,64,55,14,34,28,9};
Arrays.sort(arr2);
System.out.print("Arrays.sort After sorting:");
System.out.println(Arrays.toString(arr2));
}
}
```

## Quick sort (divide and conquer)

Fast platoon - > Division

Array. In jdk Sorts () is a fast scheduling algorithm used

### ● one pass unidirectional scanning method

Idea:

Array with two pointers:

• Scan_pos - sp, from left to right, confirm that the value is less than or equal to the principal element;
• The matrix on the right - rp, from the right interval to the left, confirms the elements whose value is greater than the principal element;

It is divided into three sections:

• The interval swept by sp is the interval less than or equal to the principal element;
• The interval swept by rp is the interval larger than the principal component;
• The interval between sp and rp is not unknown.
```import java.util.Arrays;

public class quickSort {
public static void QuickSort1(int[] arr, int l, int r) {
if(l < r) {
int q = Partition(arr, l, r);
QuickSort1(arr, l, q - 1);
QuickSort1(arr, q + 1, r);
}
}

public static int Partition(int[] arr, int p, int r) {
int pivot = arr[p];
int sp = p + 1;		// Scan pointer
int rp = r;			// Right hand pointer
while(sp <= rp) {
if(arr[sp] <= pivot)
sp++;
else {
swap(arr, sp, rp);
rp--;
}
}
swap(arr, p, rp);
return rp;
}

public static void swap(int[] arr, int a, int b) {
int temp = arr[a];
arr[a] = arr[b];
arr[b] = temp;
}

// test
public static void main(String[] args){
int[] arr = {51,2,35,64,55,14,34,28,9};

System.out.print("Before sorting:");
System.out.println(Arrays.toString(arr));
System.out.print("QuickSort1 After sorting by one-way scanning method:");
QuickSort1(arr, 0, 8);
System.out.println(Arrays.toString(arr));

int[] arr2 = {51,2,35,64,55,14,34,28,9};
Arrays.sort(arr2);
System.out.print("Arrays.sort After sorting:");
System.out.println(Arrays.toString(arr2));
}
}
```

### ● bidirectional scanning zoning method

Idea:

Scan the head and tail pointer to the middle, find the elements larger than the principal element from the left, find the elements less than or equal to the principal element from the right, exchange them, and continue scanning until there are no large elements on the left and small elements on the right.

Modify the Partition:

```public static int Partition2(int[] arr, int p, int r) {
// Optimization, choose an intermediate value between P, R and mid as the principal component
int mid = p + ((r - p) >> 1);	// Subscript in the middle of the array
int midValIdx = -1;				// Of the three is the subscript of the median
if((arr[p] >= arr[mid] && arr[p] <= arr[r]) || (arr[p] <= arr[mid] && arr[p] >= arr[r]))
midValIdx = p;
else if((arr[r] >= arr[mid] && arr[r] <= arr[p]) || (arr[r] <= arr[mid] && arr[r] >= arr[p]))
midValIdx = r;
else
midValIdx = mid;
swap(arr, p, midValIdx);

int pivot = arr[p];
int lp = p + 1;		// Left hand pointer
int rp = r;			// right
while (lp <= rp) {
while (lp <= rp && arr[lp] <= pivot) lp++;
while (lp <= rp && arr[rp] > pivot) rp--;
if(lp < rp)
swap(arr, lp, rp);
}
swap(arr, p, rp);
return rp;
}
```

### ● three pointer zoning method

Available when there are many repetitions in the primary element

Idea:

On the basis of unidirectional scanning, a pointer ep is added to point to the area with equal principal elements.

After ep points to the first element equal to pivot, sp points to the element:

• Less than the principal element, sp exchanges with ep, ep + +, sp + +;
• Equal to the principal element, sp + +;
• If it is larger than the principal element, sp exchanges with rp, rp –.

## Merge sort (divide and conquer)

Merge - > merge

Idea: divide the array into left and right sub arrays, recursively call merge to sort, and then use the auxiliary merge function to merge the two ordered sub arrays into an overall ordered array

• Time complexity: O (nlogn)
• Space complexity: it is necessary to open up an auxiliary space, which can be reused. The size is array length N
• Stability: stable
```import java.util.Arrays;

public class mergeSort {
private static int[] helper;

public static void MergeSort(int[] arr) {
// Open up auxiliary space
helper = new int[arr.length];
MergeSort(arr, 0, arr.length - 1);
}

public static void MergeSort(int[] arr, int p, int r) {
if(p < r) {
int mid = p + ((r - p) >> 1);
MergeSort(arr, p, mid);			// Sort left
MergeSort(arr, mid + 1, r);		// Sort right
merge(arr, p, mid, r);	// merge
}
}
/**
* @param arr Original array
* @param p   Low position
* @param mid Median
* @param r   high position
*/
public static void merge(int[] arr, int p, int mid, int r) {
// Copy the original array to the corresponding position in the helper
System.arraycopy(arr, p, helper, p, r-p+1);
// Three pointers
int left = p, right = mid + 1;
int current = p;
// Compare merge
while(left <= mid && right <= r) {
if(helper[left] <= helper[right]) {
arr[current++] = helper[left++];
}else {
arr[current++] = helper[right++];
}
}

// If the left row is finished and there are still items on the right that have not been compared, they need not be processed (they have been placed in the corresponding position in the arr)
// If the row on the right side is finished and there are still items not compared on the left side, move the remaining items on the left side to the arr
while(left <= mid) {
arr[current++] = helper[left++];
}
}

// test
public static void main(String[] args){
int[] arr = {51,2,35,64,55,14,34,28,9};

System.out.print("Before sorting:");
System.out.println(Arrays.toString(arr));
System.out.print("MergeSort After sorting:");
MergeSort(arr);
System.out.println(Arrays.toString(arr));

int[] arr2 = {51,2,35,64,55,14,34,28,9};
Arrays.sort(arr2);
System.out.print("Arrays.sort After sorting:");
System.out.println(Arrays.toString(arr2));
}
}
```

## Heap sort

• Binary reactor

• The key value of the parent node is always ≥ (≤) the key value of any child node
• The left and right subtrees of each node are a binary heap (both the maximum heap or the minimum heap).
• The value of any node is greater than the value of its child node - large top heap

The value of any node is less than the value of its child node - small top heap

• Heap sort

1. Heap, reverse adjustment so that each sub number is a large top heap or a small top heap

Start from n/2-1 elements to repair downward, and repair each node into a small (large) top heap

2. Output elements in sequence: swap the top and the last elements, then adjust the top elements.

The small top heap can sort the array in reverse order. Each time the top and end elements of the stack are exchanged, the top of the stack is repaired downward, so that the small elements reach the top of the heap again

• Small top pile

```// Heap
public static void makeMinHeap(int[] arr) {	// Small top pile
int n = arr.length;
for(int i = n/2-1; i >= 0; i--) {
minHeapFixDown(arr, i, n);
}
}
// Generate small top pile
public static void minHeapFixDown(int[] arr, int i, int n) {
// Left and right children of node i
int left = 2 * i + 1;
int right = 2 * i + 2;
// Judge whether the boundary is crossed
if(left >= n)	return;	// The left child crosses the boundary, that is, there is no left child, and i is the leaf node
int min = left;
if(right >= n) {	// If the right child crosses the boundary, there is no right child
min = left;
}else {		// If both left and right children exist
if(arr[left] > arr[right])
min = right;
}
// min points to the younger of the left and right children

// Judge the size relationship between i node and left and right child nodes
if(arr[i] <= arr[min])	return;	 // If i is less than children, no exchange is made
// If i is greater than children, exchange with younger children
swap(arr,i,min);
// Check and adjust the small top heap of the exchanged child nodes
minHeapFixDown(arr, min, n);
}

public static void sort(int[] arr) {
// Heap
makeMinHeap(arr);
// sort
for(int x = arr.length - 1; x >= 0; x--) {
// Swap the top of the heap (element 0) with the last element
swap(arr, 0, x);
// Reduce the scope of the heap (- 1 each time) and make the minimum heap adjustment for the elements at the top of the heap
minHeapFixDown(arr, 0, x);
}
}
```
• Large top pile
```// Large top pile
public static void makeMaxHeap(int[] arr) {
int n = arr.length;
for(int i = n/2-1; i >= 0; i--) {
MaxHeapFixDown(arr, i, n);
}
}
public static void MaxHeapFixDown(int[] arr, int i, int n) {
// Left and right children
int left = i * 2 + 1;
int right = i * 2 + 2;

// Is it out of bounds
if(left >= n) return;
int max = left;
if(right >= n) {
max = left;
}else {
if(arr[left] < arr[right])
max = right;
}
// max points to the older of the left and right children

// i node and child node to judge
if(arr[i] >= arr[max])	return;
swap(arr, i, max);
// Check and adjust the exchanged nodes
MaxHeapFixDown(arr, max, n);
}
public static void sort2(int[] arr) {
// Heap
makeMaxHeap(arr);
// sort
for(int x = arr.length-1; x >= 0; x--) {
swap(arr, 0, x);
MaxHeapFixDown(arr, 0, x);
}
}
```

For testing:

```public static void main(String[] args) {
int[] arr = {36,6,34,11,81,18,61,47,99,7};

System.out.print("Before sorting:");
System.out.println(Arrays.toString(arr));
System.out.print("After heap sorting(Reverse order): ");
sort1(arr);
System.out.println(Arrays.toString(arr));
System.out.print("After heap sorting(positive sequence): ");
sort2(arr);
System.out.println(Arrays.toString(arr));
}
```

## Count sort

Idea:

Open up a new space with the size of max(source), scan the source, take value as the subscript of the auxiliary space, and record the number of value with the change position element of the auxiliary space.

After that, you can repair the source to be arranged in ascending order by traversing the helper:

• If the value of this bit (index) is 0, it means that index has not appeared in source;

• If the value of this bit (index) is 1, it means that the index appears once in the source. If it is 2, it naturally appears twice.

Time complexity: scan the source once and the helper once, and the complexity is N+k;

Space complexity: auxiliary space K, k=maxOf(source).

Existing problems:

1. Repeating element

```// Count sort (the original array has duplicate values)
public static void sort(int[] source) {
int[] helper = new int[maxOf(source) + 1];
for(int e : source) {
helper[e]++;
}

int current = 0; //Location of data backfill
for(int i = 0; i < helper.length; i++) {
while(helper[i] > 0) {
source[current++] = i;
helper[i]--;
}
}
}
```
2. Negative number

Find out the max value and min value of the original array. The space size of the auxiliary array is max min + 1, with source[i]-min as the subscript.

```// Count sort (the original array has a negative number)
public static void CountSort(int[] source) {
int min = minOf(source);
int max = maxOf(source);
int[] helper = new int[max - min + 1];
for(int e : source) {
helper[e - min]++;
}

int current = 0;
for(int i = 0; i < helper.length; i++) {
while (helper[i] > 0) {
source[current++] = i + min;
helper[i]--;
}
}
}
```

Test:

```// Array maximum
public static int maxOf(int[] arr) {
int max = 0;
for(int i = 1; i < arr.length; i++) {
if(arr[i] > arr[max])
max = i;
}
return arr[max];
}
// Array minimum
public static int minOf(int[] arr) {
int min = 0;
for(int i = 1; i < arr.length; i++) {
if(arr[i] < arr[min])
min = i;
}
return arr[min];
}
// test
public static void main(String[] args){
int[] arr = {51,2,2,55,2,14,34,28,9};

System.out.print("Before sorting:");
System.out.println(Arrays.toString(arr));
System.out.print("After counting and sorting(Array value is repeatable,Cannot be negative): ");
sort(arr);
System.out.println(Arrays.toString(arr));

System.out.println("=====================");

int[] arr2 = {51,2,2,55,2,14,34,28,-9};
System.out.print("Before sorting:");
System.out.println(Arrays.toString(arr2));
System.out.print("After counting and sorting(Array value is repeatable,Can also be negative): ");
CountSort(arr2);
System.out.println(Arrays.toString(arr2));
}
```

## Bucket sorting

• Assign & collect
• Idea: Design K buckets (No. 0 ~ k-1), then distribute n input numbers to each bucket, sort the numbers in each bucket, and then list the elements in each bucket in order.

## Cardinality sort

• Assign & collect
• Idea: for decimal numbers, the number of each bit is in [0,9], and the number of d bits has column D. Cardinality sorting: sort according to the lower significant digits, and then sort up one by one until the end of the highest ranking sorting.
• Question: there is no 0 or negative number in the number to be arranged
```import java.util.ArrayList;
import java.util.Arrays;

// Generate 10 buckets, and the number of each bucket is variable: use array + ArrayList
private static ArrayList[] bucket = new ArrayList[10];

// Initialization bucket (static block initialization bucket, which is executed when the class is generated)
static {
for(int i = 0; i < bucket.length; i++) {
bucket[i] = new ArrayList();
}
}

// For each round of sorting, arr is the original array and d is the number of bits
public static void sort(int[] arr, int d) {
// Barrel in
for(int i = 0; i < arr.length; i++) {
putInBucket(arr[i], getDigitOn(arr[i], d));
}

// Barrel out
int current = 0;
for(int j = 0; j < bucket.length; j++) {
for(Object o : bucket[j]) {
arr[current++] = (Integer) o;
}
}

// Empty bucket
clearAll();
}

// Barrel in
public static void putInBucket(int data, int digitOn) {
switch (digitOn) {
case 0:
break;
case 1:
break;
case 2:
break;
case 3:
break;
case 4:
break;
case 5:
break;
case 6:
break;
case 7:
break;
case 8:
break;
default:
break;
}
}

// empty
public static void clearAll() {
for(ArrayList b : bucket) {
b.clear();
}
}

// sort
public static void radixSort(int[] arr) {
int d = 1;	// The bit into the bucket is initialized to 1, that is, the lowest bit
int max = maxOf(arr);	// Array maximum

// Number of digits of maximum value
int dNum = 1;
while(max / 10 != 0) {
dNum++;
max /= 10;
}

// Enter and exit the barrel according to the digit cycle
while (d <= dNum) {
sort(arr, d++);
}
}

// Array maximum
public static int maxOf(int[] arr) {
int max = 0;
for(int i = 1; i < arr.length; i++) {
if(arr[i] > arr[max])
max = i;
}
return arr[max];
}

// The i-th digit of a number (from the lower digit)
public static int getDigitOn(int x, int i) {
int e = 0;
while(i >= 1) {
e = x % 10;
x /= 10;
i--;
}
return e;
}

// test
public static void main(String[] args){
int[] arr = {51,2,2,55,2,14,34,28,9};

System.out.print("Before sorting:");
System.out.println(Arrays.toString(arr));
System.out.print("Cardinality sorting:");
System.out.println(Arrays.toString(arr));

}
}
```