排序算法笔记

总结

排序算法.png

算法详解

tips:以下算法中均按从小到大排序

一 冒泡排序/Bubble Sort

思路

采用两两比较并交换位置的思路,就仿佛泡泡一样,较大的元素会慢慢“上浮”

  • 从数列一端开始,比较相邻两个元素,如果第一个元素比第二个元素大,交换两个元素位置
  • 移动至下一对相邻元素,再次进行比较和交换,直到数列最后一个元素
  • 再次从最开始的一端开始两两比较和交换,直到数列的倒数第二个元素(因为上一次的遍历已经保证倒数第一的元素是数列中最大的)
  • 不断重复以上操作。每次重复的时候,需要比较的元素个数都减1

复杂度

  • 时间复杂度
    • 最佳:O(n)
    • 平均:O(n^2)
    • 最差:O(n^2)
  • 空间复杂度:O(1)

代码实现

public static void sort(int[] numbers) {
        for (int i = 0; i < numbers.length; i++) {
            for (int j = 0; j < numbers.length-i-1; j++) {
                if (numbers[j]> numbers[j+1]){//大数在前则交换位置
                    int temp = numbers[j+1];
                    numbers[j+1] = numbers[j];
                    numbers[j] = temp;
                }
            }
        for (int num: numbers) {
            System.out.println(num);
        }
     }
 }

二 快速排序/Quick Sort

思路

  • 选取一个合适的值,一般以头元素或尾元素
  • 以选取值为基准,将数列分成两个子数列,大于基准值的在右边,小于或等于基准值的在左边
  • 分别对基准值左右两边的的子序列重复第二步操作
  • 重复第二、第三步直到子序列中只有一个元素

复杂度

  • 时间复杂度
    • 最佳:O(nlog n)
    • 平均:O(nlog n)
    • 最差:O(n^2)
  • 空间复杂度:O(nlog n)

代码实现

public static void sort(int[] numbers,int left,int right){
        if(left>=right){
            return;
        }
        int tag = numbers[left];
        int lo = left;
        int hi = right;
        while (left<right){
            while ((tag<=numbers[right])&&(left<right)){
                right--;
            }
            numbers[left] = numbers[right];
            while ((tag>=numbers[left])&&(left<right)){
                left++;
            }
            numbers[right] = numbers[left];
        }
        numbers[left] = tag;
        sort(numbers,lo,left-1);
        sort(numbers,left+1,hi);
    }

三 插入排序/Insert Sort

思路

  • 假设数列第一个元素为已排序数列,剩余数列为未排序
  • 将待排序元素挨个插入到已排序数列中
  • 每次插入都必须保证数列是有序的,即通过比较和移动有序数列中的元素,将元素插入到合适的位置

复杂度

  • 时间复杂度
    • 最佳:O(n)
    • 平均:O(n^2)
    • 最差:O(n^2)
  • 空间复杂度:O(1)

代码实现

public static void sort(int[] numbers){
        int pre;
        int current;
        for (int i = 1; i < numbers.length ; i++) {//假定第一位已经排序,所以从number[1]开始,
            pre = i - 1;
            current = numbers[i];//记录待排序的数值
            while ((pre>=0)&&(numbers[pre]>=current)){
                numbers[pre+1] = numbers[pre];//将大于等于current的值往后挪一位
                pre--;
            }
            numbers[pre+1] = current;
        }
  }

四 希尔排序/Shell Sort

希尔排序也被称为“缩小增量排序”

思路

  • 选择一个间隔d,将数列中下标间隔为d的元素划分到同一组
  • 对每一组做选择排序
  • 减小d,并重复第一、第二步操作,直到d=1

复杂度

  • 时间复杂度
    • 最佳:O(n)
    • 平均:O(nlog n)
    • 最差:O(n^2)
  • 空间复杂度:O(1)

代码实现

public static void sort(int[] numbers){
        int div = numbers.length/2;//取第一个间隔
        while (div>0){
            for (int i = div; i < numbers.length ; i++) {//对每个区间做插入排序
                int current = numbers[i];
                int preindex = i-div;
                while ((preindex>=0)&&(numbers[preindex]>current)){
                    numbers[preindex+div] = numbers[preindex];
                    preindex -= div;
                }
                numbers[preindex+div] = current;
            }
            div = div/2;
        }
 }

五 选择排序/Select Sort

思路

  • 将数列分为已排序和未排序两部分
  • 每次均从未排序的数列中选择最小的元素,插入到已排序部分的尾部
  • 重复以上操作,知道未排序数列为空

复杂度

  • 时间复杂度:O(n^2)
  • 空间复杂度:O(1)

代码实现

public static void sort(int[] numbers){
        int index = 0;
        while (index < numbers.length-1){
            int temp = numbers[index];
            int current = index+1;
            for (int i = index+1; i < numbers.length; i++) {//查找未排序数列中的最小值
                if(temp>numbers[i]){
                    temp = numbers[i];
                    current = i;
                }
            }
            //交换。将最小值放到未排序数列最前面
            numbers[current] = numbers[index];
            numbers[index] = temp;
            index++;
        }
 }

六 堆排序/Heap Sort

思路

  • 将数列中的元素构建成最大堆,作为无序区,记为H
  • 将无序区的根元素Hn和最后一个元素H1交换位置,则Hn成为有序区,H1至H(n-1)成为新无序区
  • 调整无序区,使其仍为最大堆
  • 重复第二、第三步操作,直到所有元素都进入有序区

复杂度

  • 时间复杂度:O(nlog n)
  • 空间复杂度:O(1)

代码实现

public static void sort(int[] numbers){
        int length = numbers.length;
       //从后往前,构建最大堆
        for (int i = length/2; i >=0 ; i--) {
            toMaxHeap(numbers,i,length);
        }
        while (length>0){
            //最大值移入有序区
            int temp = numbers[0];
            numbers[0] = numbers[length-1];
            numbers[length-1] = temp;
            length--;
            toMaxHeap(numbers,0,length);
        }
    }
    /**
     * 将无序区调整为最大堆的方法
     */
    public static void toMaxHeap(int[] numbers, int index, int length){
        int left = index*2+1;
        int right = index*+2;
        int max_index = index;
        if((left<length)&&(right<length)&&numbers[left]>numbers[right]){
            max_index = left;
        }else if((left<length)&&(right<length)&&numbers[left]<=numbers[right]){
            max_index = right;
        }
        if(numbers[index]<numbers[max_index]){//不是最大堆,则调整一下
            int temp = numbers[max_index];
            numbers[max_index] = numbers[index];
            numbers[index] = temp;
            toMaxHeap(numbers,max_index,length);
        }
}

七 归并排序/Merge Sort

思路

  • 将数列平均分成两段
  • 分别对两段数列进行归并排序
  • 将两段有序的数列合并
    简单来说,就是递归然后合并。

复杂度

  • 时间复杂度:O(nlog n)
  • 空间复杂度:O(n)

代码实现

public static void sort(int[] numbers, int begin, int end){
        int mid = (begin+end)/2;
        if(begin<end){
            sort(numbers,begin,mid);//对左边进行排序
            sort(numbers,mid+1,end);//对右边进行排序
            merge(numbers,begin,mid,end);
        }
    }
    public static void merge(int[] number, int low, int mid, int high){
        int[] container = new int[high-low+1];//开辟临时存储区
        int i = low;//第一个数组的起点
        int j = mid+1;//第二个数组的起点
        int index = 0;//临时存储区的下标
        while ((i<=mid)&&(j<=high)){
            //依次将较小的数放入container
            if(number[i]<number[j]){
                container[index] = number[i];
                i++;
            }else {
                container[index] = number[j];
                j++;
            }
            index++;
        }
        while (i<=mid){
            container[index] = number[i];
            i++;
            index++;
        }
        while (j<=high){
            container[index] = number[j];
            j++;
            index++;
        }
        for (int k = 0; k < container.length; k++) {
            number[low+k] = container[k];
        }
    }

计数排序/Counting Sort

思路

非比较排序,待排序的数列需要是有限范围内的整数

  • 获得数列的最大值和最小值
  • 创建一个大小为最大值的数组,记为count
  • 将数列中数值n的数量纪录在count[n]中
  • 按count下标的顺序,反向填充原来的数列

复杂度

  • 时间复杂度:O(n+k)
  • 空间复杂度:O(k)

代码实现

public static void sort(int[] numbers){
        int large = numbers[0];
        int little = numbers[0];
        //最大最小
        for (int num:numbers) {
            if(num>large){
                large = num;
            }else if(num<little){
                little = num;
            }
        }
        int[] count = new int[large+1];
        for (int num:numbers) {
            count[num]++;
        }
        int index = 0;
        for (int i = little; i < count.length; i++) {
            while (count[i]>0){
                numbers[index++] = i;
                count[i]--;
            }
        }
    }

桶排序/Bucket Sort

思路

是对计数排序的改进。将数列按照一定的映射关系划分为大小相同的子区间,区间内元素排序,然后合并

  • 获得数列的最大值和最小值
  • ArrayList 作为桶,桶的数量为(max-min)/arr.length+1
  • 遍历数列,计算每个元素所在的桶
  • 每个桶各自排序
  • 遍历桶数组,反向填充原来的数列

复杂度

  • 时间复杂度
    • 最佳/平均:O(n+k)
    • 最差:O(n^2)
  • 空间复杂度:O(n+k)

代码实现

public static void sort(int[] numbers){
        int large = numbers[0];
        int little = numbers[0];
        //最大最小
        for (int num:numbers) {
            if(num>large){
                large = num;
            }else if(num<little){
                little = num;
            }
        }
        int bucketSize = numbers.length;//如果数据均匀的话,桶的大小不需要为数列的大小
        int bucketNum = (large - little) / bucketSize + 1;
        ArrayList<ArrayList<Integer>> buckets = new ArrayList<>(bucketNum);
        for(int i = 0; i < bucketNum; i++){
            buckets.add(new ArrayList<Integer>());
        }
        //将每个元素放入桶
        for(int i = 0; i < numbers.length; i++){
            int index = (numbers[i] - little) / bucketSize;
            buckets.get(index).add(numbers[i]);
        }
        //对每个桶进行排序
        for(int i = 0; i < buckets.size(); i++){
            Collections.sort(buckets.get(i));
        }
        int index = 0;
        for (ArrayList<Integer> list:buckets) {
            for (Integer i: list) {
                numbers[index] = i;
                index++;
            }
        }

    }

基数排序/Radix Sort

思路

  • 计算数组中的最大数,并计算其位数;
  • 对数列中的每个元素,从最低位开始取每个位组成radix数组;
  • 对radix数组进行计数排序;
  • 重复第二、第三操作直到最大位数排序结束

复杂度

  • 时间复杂度:O(n*k)
  • 空间复杂度:O(n+k)

代码实现

public static void sort(int[] numbers) {//默认10进制
        int large = numbers[0];
        //最大数
        for (int num : numbers) {
            if (num > large) {
                large = num;
            }
        }
        int digit = String.valueOf(large).length();//最大位数
        int mod = 10;
        int dev = 1;
        int bucketNum = 10;
        ArrayList<ArrayList<Integer>> buckets = new ArrayList<>(bucketNum);
        for (int i = 0; i < bucketNum; i++) {
            buckets.add(new ArrayList<Integer>());
        }
        for (int i = 0; i < digit; i++, dev *= 10, mod *= 10) {
            for (int j = 0; j < numbers.length; j++) {
                int bucket = (int) ((numbers[j] % mod) / dev);
                buckets.get(bucket).add(numbers[j]);
            }
            int index = 0;
            for (int k = 0; k < bucketNum; k++) {
                ArrayList<Integer> list = buckets.get(k);
                if (list.size()>0) {
                    for (Integer num:buckets.get(k)) {
                        numbers[index++] = num;
                    }
                    list.clear();//将桶清空
                }
            }
        }
    }
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,098评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,213评论 2 380
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 149,960评论 0 336
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,519评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,512评论 5 364
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,533评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,914评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,574评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,804评论 1 296
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,563评论 2 319
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,644评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,350评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,933评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,908评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,146评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,847评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,361评论 2 342

推荐阅读更多精彩内容