1. 冒泡排序
主要思想:外层循环从1到n-1,内循环从当前外层的元素的下一个位置开始,依次和外层的元素比较,出现逆序就交换。它重复地走访过要排序的数列,一次比较两个元素,如果他们的顺序错误就把他们交换过来。
特点:stable sort(稳定性排序)、In-place sort(不占用额外的空间,只是交换元素)
最优复杂度:当输入数组就是排好序的时候,复杂度为O(n),而快速排序在这种情况下会产生O(n^2)的复杂度。
最差复杂度:当输入数组为倒序时,复杂度为O(n^2)
插入排序比较适合用于“少量元素的数组”。
其实插入排序的复杂度和逆序对的个数一样,当数组倒序时,逆序对的个数为n(n-1)/2,因此插入排序复杂度为O(n^2)。
1.1 算法步骤
- 比较相邻的元素。如果第一个比第二个大,就交换他们两个。
- 对每一对相邻元素作同样的工作,从开始第一对到结尾的最后一对。这步做完后,最后的元素会是最大的数。
- 针对所有的元素重复以上的步骤,除了最后一个。
- 持续每次对越来越少的元素重复上面的步骤,直到没有任何一对数字需要比较。
1.2 代码
public class BubbleSort{
public int[] sort(int[] sourceArray){
int[] a = Arrays.copyOf(sourceArray,sourceArray.length);
for(int i=1;i<a.length;i++){
for(int j=0;j<a.length-i;j++){
if(a[j]>a[j+1]){
int temp = a[j+1];
a[j+1] = a[j];
a[j] = temp;
}
}
}
return a;
}
}
2. 选择排序
特性:In-place sort,unstable sort。
思想:每次找一个最小值。
最好情况时间:O(n^2)。
最坏情况时间:O(n^2)。
2.1 算法步骤
-
首先在未排序序列中找到最小(大)元素,存放到排序序列的起始位置
-
再从剩余未排序元素中继续寻找最小(大)元素,然后放到已排序序列的末尾。
-
重复第二步,直到所有元素均排序完毕。
2.2 代码
public class selectionSort{
public static void sort(int[] array){
//一共需要n-1次
for(int i = 0;i<array.length-1;i++){
int temp = i;
// 每轮需要比较的次数 N-i
for(int j = i+1;j<array.length;j++){
if(array[j]<array[temp]){
// 记录目前能找到的最小值元素的下标
temp = j;
}
}
// 将找到的最小值和i位置所在的值进行交换
if(temp !=i){
int a = array[temp];
array[temp] = array[i];
array[i] = a;
}
}
}
}
3. 插入排序
主要思想:
特点:stable sort(稳定性排序)、In-place sort(不占用额外空间)
最优复杂度:当输入数组就是排好序的时候,复杂度为O(n),而快速排序在这种情况下会产生O(n^2)的复杂度。
最差复杂度:当输入数组为倒序时,复杂度为O(n^2)
插入排序比较适合用于“少量元素的数组”。
其实插入排序的复杂度和逆序对的个数一样,当数组倒序时,逆序对的个数为n(n-1)/2,因此插入排序复杂度为O(n^2)。
3.1 算法步骤
-
将待排序序列第一个元素看做一个有序序列,把第二个元素到最后一个元素当成是未排序序列。
-
从头到尾依次扫描未排序序列,将扫描到的每个元素插入有序序列的适当位置。(如果待插入的元素与有序序列中的某个元素相等,则将待插入元素插入到相等元素的后面。)
3.2 代码
public class InsertSort {
public static void main(String []args){
int[] arr = {9,8,7,4,3,5,2,3,1,6,2};
sort(arr);
System.out.println(Arrays.toString(arr));
}
public static void sort(int[] arr){
for(int i=1;i<arr.length;i++){
for(int j=i;j>0 && arr[j]<arr[j-1];j--){
swap(arr,j-1,j);
}
}
}
/**
* 交换数组中两个元素
* @param arr
* @param i
* @param j
*/
public static void swap(int[] arr,int i ,int j){
int temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}
}
插入排序的改进:内循环发现逆序不交换,采用整体右移,直到没有逆序的时候把元素放在该位置
public class InsertSort2 {
public static void main(String[] args) {
int[] array = new int[]{2, 3, 5, 8, 9, 0, 7, 5, 1, 6, 8, 7};
sort(array);
System.out.println(Arrays.toString(array));
}
private static void sort(int[] array) {
int n = array.length;
for (int i = 1; i < n; i++) {
int key = array[i];
int j = i -1;
while (j >= 0 && array[j]>key) {
array[j + 1] = array[j];
j--;
}
array[j+1] = key;
}
}
}
4. 希尔排序
思想:基于插入排序,交换不相邻的元素已对数组的局部进行排序,并最终用插入排序将局部有序的数组排序。思想是使数组中任意间隔为h的元素都是有序的,这样的数组称为h有序数组.
特性:In-place sort,unstable sort。
思想:每次找一个最小值。
最好情况时间:O(n)。
最坏情况时间:O(n^2)。
4.1 算法步骤
-
选择一个增量序列 t1,t2,……,tk,其中 ti > tj, tk = 1;
-
按增量序列个数 k,对序列进行 k 趟排序;
-
每趟排序,根据对应的增量 ti,将待排序列分割成若干长度为 m 的子序列,分别对各子表进行直接插入排序。仅增量因子为 1 时,整个序列作为一个表来处理,表长度即为整个序列的长度。
在此我们选择增量gap=length/2,缩小增量继续以gap = gap/2的方式,这种增量选择我们可以用一个序列来表示,{n/2,(n/2)/2…1},称为增量序列。希尔排序的增量序列的选择与证明是个数学难题,我们选择的这个增量序列是比较常用的,也是希尔建议的增量,称为希尔增量,但其实这个增量序列不是最优的。此处我们做示例使用希尔增量。
4.2代码
public class Shell {
public static void main(String[] args){
int[] arr = {9,8,7,4,3,5,2,3,1,6,2};
sort(arr);
System.out.println(Arrays.toString(arr));
}
public static void sort(int[] arr){
int gap = 1;
while(gap<arr.length/3) gap=3*gap+1;//一个递增序列gap=1,4,13,40,121,364......
while(gap >= 1){ //注意这一块
for(int i = gap;i<arr.length;i++){
//这里就是插入排序;将a[i]插入到a[i-gap]、a[i-2*gap]、a[i-3*gap]......中去
for(int j=i; j>=gap && arr[j]<arr[j-gap]; j-=gap){
swap(arr,j,j-gap);
}
}
gap = gap/3;
}
}
/**
* 交换数组中两个元素
* @param arr
* @param i
* @param j
*/
public static void swap(int[] arr,int i ,int j){
int temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}
}
5. 归并排序
特点:stable sort、Out-place sort
思想:运用分治法思想解决排序问题。
最坏情况运行时间:O(nlgn)
最佳运行时间:O(nlgn)
虽然与快排渐近复杂度一样,但是归并排序的系数比快排大。
程序中merge的精髓(也就是排序):左半边用尽,则取右半边元素;右半边用尽,则取左半边元素;右半边的当前元素小于左半边的当前元素,则取右半边元素;右半边的当前元素大于左半边的当前元素,则取左半边的元素。实际上大部分发生的都是后面两句话,前面两句只是特殊情况而已。
5.1 算法步骤
-
申请空间,使其大小为两个已经排序序列之和,该空间用来存放合并后的序列;
-
设定两个指针,最初位置分别为两个已经排序序列的起始位置;
-
比较两个指针所指向的元素,选择相对小的元素放入到合并空间,并移动指针到下一位置;
-
重复步骤 3 直到某一指针达到序列尾;
-
将另一序列剩下的所有元素直接复制到合并序列尾。
5.2 代码
自顶向下
//mergeSort(a,0,a.length-1);
static void mergeSort(int[] arr,int lo, int hi){
if(lo>=hi) return;
int mid = lo + (hi-lo)/2;
mergeSort(arr,lo,mid);
mergeSort(arr,mid+1,hi);
mergeLR(arr,lo,mid,hi);
}
static void mergeLR(int[] arr,int lo ,int mid,int hi){
int[] aux = new int[arr.length];
for(int i=lo;i<=hi;i++){
aux[i] = arr[i];
}
int l=lo,r=mid+1;
for(int k=lo;k<=hi;k++){
if(l>mid){
arr[k] = aux[r++];
}else if(r>hi){
arr[k] = aux[l++];
}else if(aux[l]<aux[r]){
arr[k] = aux[l++];
}else {
arr[k] = aux[r++];
}
}
}
首先两两归并,然后四四归并,然后八八归并,一直下去。
自底向上
public class MergeSort2 {
public static void main(String[] args) {
int[] array = new int[]{2, 3, 5, 8, 9, 0, 7, 5, 1, 6, 8, 7};
sort(array);
System.out.println(Arrays.toString(array));
}
public static void sort(int[] array) {
int N = a.length;
int[] aux = new int[N];
for (int n = 1; n < N; n = n+n) {
for (int i = 0; i < N-n; i += n+n) {
int lo = i;
int m = i+n-1;
int hi = Math.min(i+n+n-1, N-1);
merge(array, aux, lo, m, hi);
}
}
}
private static void merge(int[] array, int[] aux, int lo, int mid, int hi) {
for (int k = lo; k <= hi; k++) {
aux[k] = array[k];
}
// merge back to a[]
int i = lo, j = mid+1;
for (int k = lo; k <= hi; k++) {
if(i > mid) array[k] = aux[j++]; // this copying is unneccessary
else if (j > hi) array[k] = aux[i++];
else if (aux[j]<aux[i]) array[k] = aux[j++];
else array[k] = aux[i++];
}
}
}
6.快速排序
特性:unstable sort、In-place sort。
最坏运行时间:当输入数组已排序时,时间为O(n^2),当然可以通过随机化来改进(shuffle array 或者 randomized select pivot),使得期望运行时间为O(nlgn)。
最佳运行时间:O(nlgn)
快速排序的思想也是分治法。
当输入数组的所有元素都一样时,不管是快速排序还是随机化快速排序的复杂度都为O(n^2),
6.1 算法步骤
- 从数列中挑出一个元素,称为 “基准”(pivot);
- 重新排序数列,所有元素比基准值小的摆放在基准前面,所有元素比基准值大的摆在基准的后面(相同的数可以到任一边)。 此过程可以使用两个哨兵i与j。在这个分区退出之后,该基准就处于数列的中间位置。这个称为分区(partition)操作;
- 递归地(recursive)把小于基准值元素的子数列和大于基准值元素的子数列排序。
6.2 代码
public class QuickSort{
public static void quickSort(int[] a,int left,int right){
//终止条件
while(left>right) return;
//先确定一个基准,以左边第一个元素为基准
int temp = a[left];
//指定两个哨兵,分别从左右两端出发
int i = left,j = right;
while(i!=j){
//右指针先行,找到一个比基准小的值
while(a[j]>=temp && i<j) j--;
//右指针找到后停下,左指针开始向右移动找到一个比基准大的值
while(a[i]<=temp && i<j) i++;
//右指针找到后停下,如果i,j,不相遇,交换i,j位置上的数字
if(i<j){
int t = a[i];
a[i] = a[j];
a[j] = t;
}
}
//当i,j相遇后,跳出循环,基准归位,该基准就到了自己的最终位置了
a[left] = a[i];//右指针先行,i和j交换使得a[left] > a[i]
a[i] = temp;//然后就可以交换left和i的值了
//分别对子序列进行快排
quickSort(a,left,i-1);
quickSort(a,j+1,right);
}
public static void main(String[] args) {
int [] array = {5,2,3,1,6,4,7,8,0,9};
QuickSort.quickSort(array,0,array.length - 1);
for (int i = 0; i < array.length;i++)
System.out.print(array[i] + " ");
}
}
使用荷兰国旗优化后的快排:
public class Code_04_QuickSort {
public static void quickSort(int[] arr) {
if (arr == null || arr.length < 2) {
return;
}
quickSort(arr, 0, arr.length - 1);
}
public static void quickSort(int[] arr, int l, int r) {
if (l < r) {
//随机选择一个数字
swap(arr, l + (int) (Math.random() * (r - l + 1)), r);
//先分区,找到等于的区域
int[] p = partition(arr, l, r);
//分别对大于和小于的区域快排
quickSort(arr, l, p[0] - 1);
quickSort(arr, p[1] + 1, r);
}
}
//less左边(包括less)都是小于的,more右边都是大于的,中间还有等于区域
public static int[] partition(int[] arr, int l, int r) {
int less = l - 1;
int more = r;
//该步骤可以画图助理解
while (l < more) {
//小于,则less右移一位
if (arr[l] < arr[r]) {
//这种情况下:less右边紧邻的只会是小于或者等于的数字
swap(arr, ++less, l++);
//大于,则more左移一位
} else if (arr[l] > arr[r]) {
swap(arr, --more, l);
//等于,不用交换,直接l右移一位
} else {
l++;
}
}
//最后交换more和r上的数字,则more位置变成等于的数字
swap(arr, more, r);
//返回等于的位置区间
return new int[] { less + 1, more };
}
public static void swap(int[] arr, int i, int j) {
int tmp = arr[i];
arr[i] = arr[j];
arr[j] = tmp;
}
7.堆排序
特性:unstable sort、In-place sort。
最优时间:O(nlgn)
最差时间:O(nlgn)
堆是具有以下性质的完全二叉树:每个结点的值都大于或等于其左右孩子结点的值,称为大顶堆;或者每个结点的值都小于或等于其左右孩子结点的值,称为小顶堆。如下图:
对堆中的结点按层进行编号,将这种逻辑结构映射到数组中就是下面这个样子
该数组从逻辑上讲就是一个堆结构,我们用简单的公式来描述一下堆的定义就是:
-
大顶堆:arr[i] >= arr[2i+1] && arr[i] >= arr[2i+2]
-
小顶堆:arr[i] <= arr[2i+1] && arr[i] <= arr[2i+2]
堆排序的基本思想是:将待排序序列构造成一个大顶堆,此时,整个序列的最大值就是堆顶的根节点。将其与末尾元素进行交换,此时末尾就为最大值。然后将剩余n-1个元素重新构造成一个堆,这样会得到n个元素的次小值。如此反复执行,便能得到一个有序序列了。
堆排序是一种选择排序,整体主要由构建初始堆+交换堆顶元素和末尾元素并重建堆两部分组成。其中构建初始堆经推导复杂度为O(n),在交换并重建堆的过程中,需交换n-1次,而重建堆的过程中,根据完全二叉树的性质,[log2(n-1),log2(n-2)…1]逐步递减,近似为nlogn。所以堆排序时间复杂度一般认为就是O(nlogn)级。
7.1 算法步骤
-
将无需序列构建成一个堆,根据升序降序需求选择大顶堆或小顶堆;
-
将堆顶元素与末尾元素交换,将最大元素"沉"到数组末端;
-
重新调整结构,使其满足堆定义,然后继续交换堆顶元素与当前末尾元素,反复执行调整+交换步骤,直到整个序列有序。
7.2 代码
public class HeapSort2 {
public static void main(String[] args){
int[] arr = {1,3,2,6,4,8,5,9,3,5,0};
sort(arr);
System.out.print(Arrays.toString(arr));
}
public static void sort(int[] arr){
int N = arr.length;
//1.构建最大堆
//为了复用sink()函数,可以从数组最后由右向左下沉元素,构建子堆(从倒数第二层开始,因为叶子节点已经是最大堆了),当然也可以由左向右上浮元素;
for(int j=N/2;j>0;j--){
//这里堆顶下标从1开始
sink(arr,j,N);
}
//2.将最大堆的堆顶元素与尾部元素交换,重新调整平衡
while(N>1){//N代表前N个元素
exch(arr,1,N--);//(1-N)对应数组(0 - N-1)
//将堆顶的元素下沉,调整最大堆
sink(arr,1,N);
}
}
//元素下沉(堆元素序号从 1 开始,简化操作
public static void sink(int[] arr,int k,int N){
while(2*k<=N){
int j=2*k;
if(j<N && less(arr,j,j+1)) j++;//j<N说明存在j+1=N下标(下标从1 - N)
if(!less(arr,k,j)) break;
exch(arr,k,j);
k=j;
}
}
//元素上浮
// public static void swim(int[] arr,int k,int N){
// while(k>1 && less(arr,k/2,k)){
// exch(arr,k,k/2);
// k = k/2;
// }
// }
//比较方法
private static Boolean less(int[] arr,int i,int j){
if(arr[i-1]<arr[j-1]) return true;
else return false;
}
//交换方法
private static void exch(int[] arr,int i,int j){
int temp = arr[i-1];
arr[i-1] = arr[j-1];
arr[j-1] = temp;
}
}
public class Code_03_HeapSort {
public static void heapSort(int[] arr) {
if (arr == null || arr.length < 2) {
return;
}
for (int i = 0; i < arr.length; i++) {
//构建一个大根堆,抽象一个二叉树,物理上是数组
heapInsert(arr, i);
}
int size = arr.length;
//堆顶元素和最后一个元素交换,则物理数组最后一个是最大的数字
swap(arr, 0, --size);
while (size > 0) {
//将新的堆顶元素下沉,重新构建大顶堆
heapify(arr, 0, size);
//循环交换堆顶和堆尾元素
swap(arr, 0, --size);
}
}
//元素上浮插入形成大根堆
public static void heapInsert(int[] arr, int index) {
while (arr[index] > arr[(index - 1) / 2]) {
swap(arr, index, (index - 1) / 2);
index = (index - 1) / 2;
}
}
//元素下沉构建大根堆
public static void heapify(int[] arr, int index, int size) {
int left = index * 2 + 1;
while (left < size) {
int largest = left + 1 < size && arr[left + 1] > arr[left] ? left + 1 : left;
largest = arr[largest] > arr[index] ? largest : index;
if (largest == index) {
break;
}
swap(arr, largest, index);
index = largest;
left = index * 2 + 1;
}
}
public static void swap(int[] arr, int i, int j) {
int tmp = arr[i];
arr[i] = arr[j];
arr[j] = tmp;
}
8.计数排序
特性:stable sort、out-place sort。
最坏情况运行时间:O(n+k)
最好情况运行时间:O(n+k)
计数排序不是基于比较的排序算法,其核心在于将输入的数据值转化为键存储在额外开辟的数组空间中。 作为一种线性时间复杂度的排序,计数排序要求输入的数据必须是有确定范围的整数。
8.1 算法步骤
- 找出待排序的数组中最大和最小的元素;
- 统计数组中每个值为i的元素出现的次数,存入数组C的第i项;
- 对所有的计数累加(从C中的第一个元素开始,每一项和前一项相加);
- 反向填充目标数组:将每个元素i放在新数组的第C(i)项,每放一个元素就将C(i)减去1。
8.2 代码
public class CountingSort {
public static void main(String[] args) throws Exception {
int[] array = { 9, 9, 8, 8, 7, 5, 3, 2, 6, 0, 5 };
int[] sort = sort(array, 9);
System.out.println(Arrays.toString(sort));
}
/**
* 输入数组的元素都是介于0..k之间的
* @param data 待排序数组
* @param k 最大元素
* @return 排序结果
*/
public static int[] sort(int[] data, int k) {
// 存放临时数据的数组tmp,初始元素都是0;k为数组中最大元素
int[] tmp = new int[k + 1];
// 计算数组中每个元素i出现的次数,存入数组tmp中的第i项,即原数组中的元素值为tmp数组中的下标
for (int i = 0; i <= data.length - 1; i++) {
tmp[data[i]]++;
}
// 计算数组中小于等于每个元素的个数,即从tmp中的第一个元素开始,每一项和前一项相加
for (int j = 1; j <= k; j++) {
tmp[j] = tmp[j] + tmp[j - 1];
}
// result数组用来来存放排序结果
int[] result = new int[data.length];
for (int i = data.length - 1; i >= 0; i--) {
result[tmp[data[i]] - 1] = data[i];
tmp[data[i]]--;
}
return result;
}
}
9.桶排序
假设输入数组的元素都在[0,1)之间。
特性:out-place sort、stable sort。
最坏情况运行时间:当分布不均匀时,全部元素都分到一个桶中,则O(n^2),当然[算法导论8.4-2]也可以将插入排序换成堆排序、快速排序等,这样最坏情况就是O(nlgn)。
最好情况运行时间:O(n)
桶排序是计数排序的升级版。它利用了函数的映射关系,高效与否的关键就在于这个映射函数的确定。桶排序 (Bucket sort)的工作的原理:假设输入数据服从均匀分布,将数据分到有限数量的桶里,每个桶再分别排序(有可能再使用别的排序算法或是以递归方式继续使用桶排序进行排)。
9.1 算法步骤
设置一个定量的数组当作空桶;
遍历输入数据,并且把数据一个一个放到对应的桶里去;
对每个不是空的桶进行排序;
从不是空的桶里把排好序的数据拼接起来。
9.2代码
public static void bucketSort(int[] arr) {
if (arr == null || arr.length < 2) {
return;
}
int max = Integer.MIN_VALUE;
for (int i = 0; i < arr.length; i++) {
max = Math.max(max, arr[i]);
}
int[] bucket = new int[max + 1];
for (int i = 0; i < arr.length; i++) {
bucket[arr[i]]++;
}
int i = 0;
for (int j = 0; j < bucket.length; j++) {
while (bucket[j]-- > 0) {
arr[i++] = j;
}
}
}
10.基数排序
特性:stable sort、Out-place sort。
最坏情况运行时间:O((n+k)d)
最好情况运行时间:O((n+k)d)
基数排序是按照低位先排序,然后收集;再按照高位排序,然后再收集;依次类推,直到最高位。有时候有些属性是有优先级顺序的,先按低优先级排序,再按高优先级排序。最后的次序就是高优先级高的在前,高优先级相同的低优先级高的在前。
10.1 算法步骤
取得数组中的最大数,并取得位数;
arr为原始数组,从最低位开始取每个位组成radix数组;
对radix进行计数排序(利用计数排序适用于小范围数的特点);
10.2 代码
//基数排序
public class Code_07_RadixSort {
// only for no-negative value
public static void radixSort(int[] arr) {
if (arr == null || arr.length < 2) {
return;
}
radixSort(arr, 0, arr.length - 1, maxbits(arr));
}
public static int maxbits(int[] arr) {
int max = Integer.MIN_VALUE;
for (int i = 0; i < arr.length; i++) {
max = Math.max(max, arr[i]);
}
int res = 0;
while (max != 0) {
res++;
max /= 10;
}
return res;
}
public static void radixSort(int[] arr, int begin, int end, int digit) {
final int radix = 10;
int i = 0, j = 0;
int[] count = new int[radix];
int[] bucket = new int[end - begin + 1];
for (int d = 1; d <= digit; d++) {
for (i = 0; i < radix; i++) {
count[i] = 0;
}
for (i = begin; i <= end; i++) {
j = getDigit(arr[i], d);
count[j]++;
}
for (i = 1; i < radix; i++) {
count[i] = count[i] + count[i - 1];
}
for (i = end; i >= begin; i--) {
j = getDigit(arr[i], d);
bucket[count[j] - 1] = arr[i];
count[j]--;
}
for (i = begin, j = 0; i <= end; i++, j++) {
arr[i] = bucket[j];
}
}
}
public static int getDigit(int x, int d) {
return ((x / ((int) Math.pow(10, d - 1))) % 10);
}
// for test
public static void comparator(int[] arr) {
Arrays.sort(arr);
}
// for test
public static int[] generateRandomArray(int maxSize, int maxValue) {
int[] arr = new int[(int) ((maxSize + 1) * Math.random())];
for (int i = 0; i < arr.length; i++) {
arr[i] = (int) ((maxValue + 1) * Math.random());
}
return arr;
}
// for test
public static int[] copyArray(int[] arr) {
if (arr == null) {
return null;
}
int[] res = new int[arr.length];
for (int i = 0; i < arr.length; i++) {
res[i] = arr[i];
}
return res;
}
// for test
public static boolean isEqual(int[] arr1, int[] arr2) {
if ((arr1 == null && arr2 != null) || (arr1 != null && arr2 == null)) {
return false;
}
if (arr1 == null && arr2 == null) {
return true;
}
if (arr1.length != arr2.length) {
return false;
}
for (int i = 0; i < arr1.length; i++) {
if (arr1[i] != arr2[i]) {
return false;
}
}
return true;
}
// for test
public static void printArray(int[] arr) {
if (arr == null) {
return;
}
for (int i = 0; i < arr.length; i++) {
System.out.print(arr[i] + " ");
}
System.out.println();
}
// for test
public static void main(String[] args) {
int testTime = 500000;
int maxSize = 100;
int maxValue = 100000;
boolean succeed = true;
for (int i = 0; i < testTime; i++) {
int[] arr1 = generateRandomArray(maxSize, maxValue);
int[] arr2 = copyArray(arr1);
radixSort(arr1);
comparator(arr2);
if (!isEqual(arr1, arr2)) {
succeed = false;
printArray(arr1);
printArray(arr2);
break;
}
}
System.out.println(succeed ? "Nice!" : "Fucking fucked!");
int[] arr = generateRandomArray(maxSize, maxValue);
printArray(arr);
radixSort(arr);
printArray(arr);
}
}