Java中10个常用的排序算法

Java中10个常用的排序算法

摘要:算法在日常的编程中是非常重要的一个知识点,今天让我们一起重温排序,下面是用java实现的常用的十个算法案例:

import java.util.ArrayList;
import java.util.Arrays;

public class Sort {

    public static void main(String[] args) {
        int[] arr = new int[20];
        int index = 0;
        for(int i = 20;i > 0;i--)
            arr[index++] = i;
        System.out.println("原数组:");
        System.out.println(Arrays.toString(arr));
        System.out.println("开始排序");
        arr = InsertionSort(arr);
        System.out.println("排序后为:");
        System.out.println(Arrays.toString(arr));
    }

    // 工具:交换数组中元素的位置
    public static int[] swap(int[] arr, int i, int j){
        int temp = arr[i];
        arr[i] = arr[j];
        arr[j] = temp;
        return arr;
    }

    // ****** 1.直接插入排序 ******
    public static int[] InsertionSort(int[] arr){
        if(arr.length == 0 || arr.length == 1)
            return arr;
        for(int i = 0;i < arr.length - 1;i++){
            // 将 i+1 位置的数插入 0 到 i 之间的数组,从后往前遍历
            // current 指 i+1 的位置元素,pre 指 0 到 i 中依次向前遍历的指针
            int current = arr[i+1];
            int pre = i;
            while(pre >= 0 && current < arr[pre]){
                arr[pre+1] = arr[pre];
                pre--;
            }
            // 最后将原来 i+1 位置的元素放入现在 0 到 i+1 之间数组中正确的位置上
            // pre+1 是因为刚才循环结束时又自减了一次
            arr[pre+1] = current;
            // 打印这一轮的排序结果
            System.out.println(Arrays.toString(arr));
        }
        return arr;
    }

    // ****** 2.希尔排序 ******
    // 希尔排序最重要的变量就是 gap,所有需要+1或者自加1的地方都要注意
    public static int[] ShellSort(int[] arr){
        if(arr.length == 0 || arr.length == 1)
            return arr;
        int current, gap = arr.length / 2;
        while(gap > 0){
            for(int  i = gap;i < arr.length;i++){
                // 将 pre+gap 位置的数插入 0 到 pre 之间“同组”的数组,从后往前遍历
                // current 指 pre+gap 的位置元素
                current = arr[i];
                int pre = i - gap;
                while(pre >= 0 && arr[pre] > current){
                    arr[pre+gap] = arr[pre];
                    pre -= gap;
                }
                arr[pre+gap] = current;
                // 打印这一轮的排序结果
                System.out.println(Arrays.toString(arr));
            }
            gap /= 2;
        }
        return arr;
    }

    // ****** 3.简单选择排序 ******
    public static int[] SelectionSort(int[] arr){
        if(arr.length == 0 || arr.length == 1)
            return arr;
        for(int i = 0;i < arr.length - 1;i++){
            // 每一轮挑出一个最小的元素,依次与不断增长的 i 位置的元素交换
            int MinIndex = i;
            for(int j = i;j < arr.length;j++){
                if(arr[j] < arr[MinIndex])
                    MinIndex = j;
            }
            arr = swap(arr,MinIndex,i);
            // 打印这一轮的排序结果
            System.out.println(Arrays.toString(arr));
        }
        return arr;
    }

    // ****** 4.堆排序 ******
    // 主函数
    public static int[] HeapSort(int[] arr){
        if(arr.length == 0 || arr.length == 1)
            return arr;
        int len = arr.length;
        // 堆排序第一步是先把当前数组变成一个最大堆
        arr = AdjustMaxHeap(arr, len-1);
        while(len > 0){
            // 取出堆顶的元素(最大元素)与末尾还没有确定位置的元素交换
            arr = swap(arr,0,len - 1);
            // 打印这一轮的排序结果
            System.out.println(Arrays.toString(arr));
            len--;
            arr = AdjustMaxHeap(arr,len - 1);
            }
        return arr;
    }

    // 调整为最大堆
    public static int[] AdjustMaxHeap(int[] arr, int lastIndex){
        for(int i = (lastIndex - 1) / 2;i>=0;i--){
            arr = AdjustLocalHeap(arr,lastIndex,i);
        }
        return arr;
    }

    //调整局部堆使其成为局部最大堆
    /*
     注意事项:堆中结点是从 1 开始的,但把数组看作堆的话,数组的下标是从 0 开始的
     那么父结点与子结点的关系就会发生变化:
        父结点 = (子结点-1)/2
        左子结点 = 父结点*2+1
        右子结点 = 父结点*2+2
     */
    public static int[] AdjustLocalHeap(int[] arr,int lastIndex,int i){
        // 找出当前结点和左右子结点(如果有左右子结点的话)中最大的元素,让这个最大的元素成为父结点
        int maxIndex = i;
        if(i*2+1 <= lastIndex && arr[i] < arr[i*2+1])
            maxIndex = i*2+1;
        // 这里要多一个右子结点是否大于左子结点的判定
        if(i*2+2 <= lastIndex && arr[i] < arr[i*2+2] && arr[i*2+1] < arr[i*2+2])
            maxIndex = i*2+2;
        // 如果父结点不是三个结点中的最大结点,那么将最大结点变成父结点
        // 再通过递归看看这个比较小的父结点能不能再“往下沉”
        if(maxIndex != i){
            arr = swap(arr,maxIndex,i);
            arr = AdjustLocalHeap(arr,lastIndex,maxIndex);
        }
        return arr;
    }

    // ****** 5.冒泡排序 ******
    public static int[] BubbleSort(int[] arr){
        if(arr.length == 0 || arr.length ==1)
            return arr;
        for(int i = arr.length-1;i > 0;i--){
            for(int j = 1;j <= i;j++){
                if(arr[j] < arr[j-1])
                    arr = swap(arr,j,j-1);
            }
            // 打印这一轮的排序结果
            System.out.println(Arrays.toString(arr));
        }
        return arr;
    }

    // ****** 6.快速排序 ******
    //主函数
    public static int[] QuickSort(int[] arr){
        if(arr.length == 0 || arr.length ==1)
            return arr;
        arr = LocalQuickSort(arr,0,arr.length -1 );
        return arr;
    }

    // 快速排序
    public static int[] LocalQuickSort(int[] arr, int start, int last){
        if(start >= last)
            return arr;
        // benchmark 指基准数,也就是这一轮将要确定位置的数
        int benchmark = start;
        int left = start;
        int right = last;
        while(left < right){
            // 必须右指针先走
            while(arr[right] > arr[benchmark] && left < right) right--;
            if(arr[right] <= arr[benchmark] && left < right) arr[left++] = arr[right];
            while(arr[left] < arr[benchmark] && left < right) left++;
            if(arr[right] >= arr[benchmark] && left < right) arr[right--] = arr[left];
        }
        arr[left] = arr[benchmark];
        // 打印这一轮的排序结果
        System.out.println(Arrays.toString(arr));
        // 通过递归,分别对已确定位置的数的两边区域进行快速排序
        arr = LocalQuickSort(arr,start,left-1);
        arr = LocalQuickSort(arr,left+1,last);
        return arr;
    }

    // ****** 7.归并排序 ******
    // 主函数
    public static int[] MergeSort(int[] arr){
        if(arr.length == 0 || arr.length ==1)
            return arr;
        arr = Merge(arr,0,arr.length-1);
        return arr;
    }

    // 归并排序
    public static int[] Merge(int[] arr,int start,int last){
        // start < last 的判断意味着 arr 指定的范围内必须至少有两个元素
        if(start < last){
            int mid = (start + last) / 2;
            // 左右部分分别递归
            arr = Merge(arr,start,mid);
            arr = Merge(arr,mid+1,last);
            // 递归层面:从里往外依次将左半部分和右半部分整合成一个部分
            arr = merge(arr,start,mid,last);
        }
        return arr;
    }

    public static int[] merge(int[] arr,int start,int mid,int last){
        // tempArr 指一个额外数组,用来临时给 arr 中同一区域的元素排序
        int[] tempArr = new int[arr.length];
        // p1 指 arr 指定区域的左半部分的指针,p2 指 arr 指定区域的右半部分的指针,p 指额外数组 tempArr 的指针
        int p1 = start, p2 = mid+1, p = start;
        // 从指定区域的左右半部分中取出最小元素放入额外数组,完成指定区域内的排序
        while(p1 <= mid && p2 <= last){
            if(arr[p1] <= arr[p2])
                tempArr[p++] = arr[p1++];
            else
                tempArr[p++] = arr[p2++];
        }
        while(p1 <= mid) tempArr[p++] = arr[p1++];
        while(p2 <= last) tempArr[p++] = arr[p2++];
        // 将额外数组中的数据覆盖到原 arr 数组中
        for(int i = start;i <= last;i++)
            arr[i] = tempArr[i];
        System.out.println(Arrays.toString(arr));
        return arr;
    }

    // ****** 8.基数排序 ******
    public static int[] RadixSort(int[] arr){
        if(arr.length == 0 || arr.length ==1)
            return arr;
        // max 指数组中最大的数,maxDigit 指这个最大的数是几位数
        int max = arr[0];
        for(int x:arr)
            max = Math.max(x,max);
        int maxDigit = 0;
        while(max != 0){
            max /= 10;
            maxDigit++;
        }
        // mod 用于为数组中的数取余数,div 用于把通过 mod 取的余数变成个位数
        int mod = 10;
        int div = 1;
        ArrayList<ArrayList<Integer>> bucket = new ArrayList<ArrayList<Integer>>();
        for(int j = 0;j < 10;j++){
            bucket.add(new ArrayList<Integer>());
        }
        for(int i = 0;i<maxDigit;i++,mod *= 10,div *= 10){
            // 打印这一轮的排序结果
            System.out.println(Arrays.toString(arr));
            for(int j = 0;j < arr.length;j++){
                // num 指当前元素 arr[j] 的个/十/百/千位是几
                int num = (arr[j] % mod) / div;
                bucket.get(num).add(arr[j]);
            }
            int index = 0;
            for(int j = 0;j < 10;j++){
                if(bucket.get(j).size() != 0){
                    for(int x:bucket.get(j))
                        arr[index++] = x;
                    // 将桶中所有的动态数组清空,否则第二次循环开始再用到这些动态数组时里面还会有数据
                    bucket.get(j).clear();
                }
            }
        }
        return arr;
    }

    // ****** 9.计数排序 ******
    public static int[] CountingSort(int[] arr){
        if(arr.length ==0 || arr.length == 1)
            return arr;
        int min, max;
        min = max = arr[0];
        for(int x: arr){
            if(x > max)
                max = x;
            if(x < min)
                min = x;
        }
        // bucket 指用来存储每个元素出现次数的桶,长度为元素的范围
        int[] bucket = new int[max - min +1];
        // 把 bucket 用 0 填满,因为之后要累加
        Arrays.fill(bucket,0);
        // 在 bucket 中相应的位置记录每个元素出现的次数
        for(int x:arr){
            bucket[x - min]++;
        }
        int index = 0;
        // 依次从 bucket 中提取元素覆盖到原来的 arr 上
        for(int i =0;i<bucket.length;i++){
            while(bucket[i] != 0){
                arr[index++] = i + min;
                bucket[i]--;
            }
        }
        return arr;
    }

    // ****** 10.桶排序 ******
    // 主函数
    public static int[] BucketSort(int[] arr){
        if(arr.length == 0 || arr.length == 1)
            return arr;
        arr = Bucket(arr,5);
        return  arr;
    }

    // 桶排序
    // bucketSize 指每个桶的容量,bucketCount 指桶的个数
    public static int[] Bucket(int[] arr,int bucketSize){
        int min,max;
        min = max = arr[0];
        for(int x:arr){
            if(x > max)
                max = x;
            if(x > min)
                min = x;
        }
        int bucketCount = (max - min) / bucketSize +1;
        ArrayList<ArrayList<Integer>> bucket = new ArrayList<ArrayList<Integer>>();
        for(int i = 0;i < bucketCount;i++)
            bucket.add(new ArrayList<Integer>());
        for(int x: arr){
            // 遍历每个桶
            for(int bucketIndex = 0;bucketIndex < bucketCount;bucketIndex++){
                // 如果 arr 当前元素在该桶的范围内,则将该元素放入该桶内,并结束遍历每个桶的循环
                if(x >= min + bucketIndex*bucketSize && x < min + (bucketIndex+1)*bucketSize){
                    bucket.get(bucketIndex).add(x);
                    break;
                }
            }
        }
        int index = 0;
        for(int i = 0;i < bucketCount;i++){
            // 对每个桶使用直接插入排序,调整桶内元素的顺序
            bucket.set(i,InsertionSortOfArrayList(bucket.get(i)));
            for(int x:bucket.get(i))
                arr[index++] = x;
        }
        return arr;
    }
    
    // 针对动态数组的直接插入排序
    public static ArrayList<Integer> InsertionSortOfArrayList(ArrayList<Integer> arr){
        if(arr.size() == 0 || arr.size() ==1)
            return arr;
        int current;
        int pre;
        for(int i = 0;i < arr.size() - 1;i++){
            pre = i;
            current = arr.get(i+1);
            while(arr.get(pre) > current && pre >= 0){
                arr.set(pre+1,arr.get(pre));
                pre--;
            }
            arr.set(pre+1,current);
        }
        return arr;
    }
}
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 214,875评论 6 496
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,569评论 3 389
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 160,475评论 0 350
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,459评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,537评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,563评论 1 293
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,580评论 3 414
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,326评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,773评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,086评论 2 330
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,252评论 1 343
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,921评论 5 338
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,566评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,190评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,435评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,129评论 2 366
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,125评论 2 352

推荐阅读更多精彩内容