算法 ----排序算法

1、排序算法有哪些?

插入排序: 直接插入排序、希尔排序
选择排序: 简单选择排序、堆排序
交换排序:冒泡排序、快速排序
归并排序:
基数排序:

2、最快的排序算法是哪个?

  快速排序:是目前基于比较的内部排序中被认为是最好的方法,当待排序的关键字是随机分布时,快速排序的平均时间最短;

3、冒泡排序

思想:
  在要排序的一组数中,对当前还未排好序的范围内的全部数,自上而下对相邻的两个数依次进行比较和调整,让较大的数往下沉,较小的往上冒。即:每当两相邻的数比较后发现它们的排序与排序要求相反时,就将它们互换。

冒泡.png

代码实现:

@Test
    public void bubbleSortTest() {
        int a[] = {3, 1, 2, 6, 4, 2};
        BubbleSort bubbleSort = new BubbleSort();
        bubbleSort.bubbleSort(a, a.length);
    }

/**
     * 冒泡排序
     * @param a
     * @param n
     */
    public void bubbleSort(int a[], int n){
        for(int i =0 ; i< n-1; i++) {
            for(int j = 0; j < n-i-1; j++) {
                if(a[j] > a[j+1])
                {
                    int tmp = a[j] ; a[j] = a[j+1] ;  a[j+1] = tmp;
                }
            }
        }
        for(int i =0 ; i< n; i++) {
            System.out.print("\t\t"+a[i]);
        }
    }

输出结果:


冒泡输出.png

冒泡排序优化:
  对冒泡排序常见的改进方法是加入一标志性变量exchange,用于标志某一趟排序过程中是否有数据交换,如果进行某一趟排序时并没有进行数据交换,则说明数据已经按要求排列好,可立即结束排序,避免不必要的比较过程。对上述代码优化如下:

 @Test
    public void bubbleSortTest() {
        int a[] = {3, 1, 2, 6, 4, 2,8,3,4,5,66,3,2,1,5,5,3,7};
        BubbleSort bubbleSort = new BubbleSort();
        bubbleSort.bubbleSort(a, a.length);
    }

/**
     * 冒泡排序
     *
     * @param a
     * @param n
     */
    public void bubbleSort(int a[], int n) {
        int pos = 0;
        for (int i = 0; i < n-1; i++) {
            for (int j = 0; j < n - i - 1; j++) {
                if (a[j] > a[j + 1]) {
                    pos++;
                    int tmp = a[j];
                    a[j] = a[j + 1];
                    a[j + 1] = tmp;
                }
            }
            if (pos == 0) {
                System.out.print("排序好了:第" + (i+1) + "趟");
                break;
            }
            pos = 0;
        }
        for (int i = 0; i < n; i++) {
            System.out.print("\t\t" + a[i]);
        }
    }

输出如下:


冒泡优化.png

可以看到18组数据外层只排了13次就已经排序完成,优化了代码效率。

4、快速排序

思想:
1)选择一个基准元素,通常选择第一个元素或者最后一个元素,
2)通过一趟排序将待排序的记录分割成独立的两部分,其中一部分记录的元素值均比基准元素值小。另一部分记录的 元素值比基准值大。
3)此时基准元素在其排好序后的正确位置
4)然后分别对这两部分记录用同样的方法继续进行排序,直到整个序列有序。
(a)一趟排序的过程:


快排一趟.png

(b)排序的全过程:


快排全过程.png

代码实现:

 @Test
    public void quickSort() {
        int a[] = {3,1,5,7,2,4,9,6,10,8};
        QuickSort quickSort = new QuickSort();
        System.out.print("brefore:");
        quickSort.print(a, a.length);
        quickSort.quickSort(a,0,a.length-1);
        System.out.print("after:");
        quickSort.print(a, a.length);
    }

 /**
     * 快排
     *
     * @param a
     * @param low
     * @param high
     */
    public void quickSort(int a[], int low, int high) {
        if (low < high) {
            int privotLoc = partition(a, low, high);  //将表一分为二
            quickSort(a, low, privotLoc - 1);          //递归对低子表递归排序
            quickSort(a, privotLoc + 1, high);        //递归对高子表递归排序
        }
    }

    private int partition(int a[], int low, int high) {
        int privotKey = a[low];                             //基准元素
        while (low < high) {                                   //从表的两端交替地向中间扫描
            while (low < high && a[high] >= privotKey)
                --high;  //从high 所指位置向前搜索,至多到low+1 位置。将比基准元素小的交换到低端
            swap(a,low,high);

            while (low < high && a[low] <= privotKey) ++low;
            swap(a,low,high);
        }
        print(a, a.length);
        return low;
    }

    private void swap(int a[], int low, int high) {
        int tmp = a[low];
        a[low] = a[high];
        a[high] = tmp;
    }

    public void print(int a[], int n) {
        for (int j = 0; j < n; j++) {
           System.out.print(a[j]+"\t");
        }
        System.out.print("\n");
    }

上面代码中很重要的是

while (low < high) {                                   //从表的两端交替地向中间扫描
            while (low < high && a[high] >= privotKey)
                --high;  //从high 所指位置向前搜索,至多到low+1 位置。将比基准元素小的交换到低端
            swap(a,low,high);

            while (low < high && a[low] <= privotKey) ++low;
            swap(a,low,high);
        }

对后排依次递减扫描,扫描到后面的小于基线位置数时把其放到基线前面;对前排递增扫描,扫描到前排大于基线位置,停止while,交换当前扫描前段以及后段。最后返回动态的基线最新位置。

输出结果:


快排结果.png

  快速排序是通常被认为在同数量级(O(nlog2n))的排序方法中平均性能最好的。但若初始序列按关键码有序或基本有序时,快排序反而蜕化为冒泡排序。为改进之,通常以“三者取中法”来选取基准记录,即将排序区间的两个端点与中点三个记录关键码居中的调整为支点记录。快速排序是一个不稳定的排序方法。

5、选择排序

思想:
  在要排序的一组数中,选出最小(或者最大)的一个数与第1个位置的数交换;然后在剩下的数当中再找最小(或者最大)的与第2个位置的数交换,依次类推,直到第n-1个元素(倒数第二个数)和第n个元素(最后一个数)比较为止。

排序过程:(由于绘图比较麻烦,下面直接用代码控制台输出排序过程了)


select.png

实现代码:

@Test
    public void selectSort(){
        int a[] = {3,1,5,7,2,4,9,6,10,8};
        SelectSort selectSort = new SelectSort();
        System.out.print("Select brefore:");
        selectSort.print(a, a.length);
        selectSort.SelectSort(a,a.length);
        System.out.print("Select after:");
        selectSort.print(a, a.length);
    }

/**
     * 选择排序
     * 第一趟:从数组中选出最小(或者最大)的一个数与第1个位置的数交换
     *
     * @param a
     * @param n
     */
    public void SelectSort(int a[], int n) {
        for (int i = 0; i < n; i++) {
            int minVaule = a[i]; //一趟预置最小值
            int temp;
            for (int j = i + 1; j < n; j++) {
                if (minVaule > a[j]) {
                    temp = a[j];
                    a[j] = minVaule;
                    minVaule = temp;
                }
            }

            a[i] = minVaule;   //把一趟的结果存起来
            printTemp(a, i);
        }
    }

    public void printTemp(int a[], int i) {
        System.out.print("第" + (i + 1) + "趟:");
        for (int j = 0; j < a.length; j++) {
            if (j == 0) {
                System.out.print("[" + a[j] + "\t");
            } else if (j == i) {
                System.out.print(a[j] + "]\t");
            } else {
                System.out.print(a[j] + "\t");
            }
        }
        System.out.print("\n");
    }

    public void print(int a[], int n) {
        for (int j = 0; j < n; j++) {
            System.out.print(a[j] + "\t");
        }
        System.out.print("\n");
    }

选择排序算法优化——二元选择排序
  上述排序一趟只记录一个最小值,如果我们一趟排序分别记录最小值和最大值,就可以减半我们外层循环次数,这种优化即二元选择排序

排序过程:


image.png

比上面一种直接选择排序少循环了一半的次数,只增加了最大值的选择过程。

代码实现:

@Test
    public void SmartSelectSort() {
        int a[] = {3, 1, 5, 7, 2, 4, 9, 6, 10, 8};
        SelectSort selectSort = new SelectSort();
        System.out.print("smart Select brefore:");
        selectSort.print(a, a.length);
        selectSort.SmartSelectSort(a, a.length);
        System.out.print("smart Select after:");
        selectSort.print(a, a.length);
    }

public void SmartSelectSort(int a[], int n) {
        for (int i = 0; i < n / 2; i++) {
            int minVaule = a[i]; //一趟预置最小值--第一个
            int maxVaule = a[n - i - 1]; //一趟预置最大值--最后一个
            int temp;
            for (int j = i ; j < n - i; j++) {
                if (minVaule > a[j]) {
                    temp = a[j];
                    a[j] = minVaule;
                    minVaule = temp;
                }

                if (maxVaule < a[j]) {
                    temp = a[j];
                    a[j] = maxVaule;
                    maxVaule = temp;
                }
            }

            a[i] = minVaule;   //把一趟的结果存起来
            a[n - i - 1] = maxVaule;
            printSmart(a, i + 1);
        }
    }

    public void printSmart(int a[], int i) {
        System.out.print("第" + i + "趟:");
        for (int j = 0; j < a.length; j++) {
            if (j == 0) {
                System.out.print("[" + a[j] + "\t");
            } else if (j == i - 1) {
                System.out.print(a[j] + "]\t");
            } else if (j == a.length - i) {
                System.out.print("[" + a[j] + "\t");
            } else if (j == a.length - 1) {
                System.out.print(a[j] + "]");
            } else {
                System.out.print(a[j] + "\t");
            }
        }
        System.out.print("\n");
    }
6、堆排序

思想:
  堆排序也是选择排序的一种,是一种树形的选择排序。堆分为最大堆和最小堆,是一棵完全二叉树,最大堆要求父节点不小于其左右子节点,最小堆要求其父节点不大于其左右子节点,对于左右子节点相互间的大小没有条件限制。

排序过程:(图片网上找的)
给定一个无序的列表数组 [16,7,3,20,17,8]对其进行堆排序,主要经过以下几个步骤:
1、根据数组元素构造一个完全二叉树

1.png

2、构造初始化堆
2.png

这个过程是从最后一个节点开始往上逐渐调整最大堆,每次比较会比较三个数,如果父节点的数比子节点小,则会拿子节点中较大的数与父节点的数进行交换,从右往左,从下往上重复进行次操作,但到最后我们发现依然不是一个大顶堆,因为16小于17,这个时候就需要对不满足条件的子节点进行调整
3.png

所以我们一次构造的初始化堆要进行检测,如果不满足大顶堆要求,我们要重新进行调整,直到行成大顶堆。

3、堆顶元素R[1]与最后一个元素R[n]交换,交换后堆长度减一

4.png

经过上述步骤,我们相当于完成了选择排序的一次外层循环,找出了一次循环中数组中最大的一个数。

4、对剩余的数进行筛选,构造新的大顶堆(从上往下)

5.png

特别注意: 初始化大顶堆时 是从最后一个有子节点开始往上调整最大堆。而堆顶元素(最大数)与堆最后一个数交换后,需再次调整成大顶堆,此时是从上往下调整的。这也是先前非要把堆筛选成完全大顶堆的原因,想想第二次寻找最大值的时候对于除了顶部元素其他都已经构建好的堆,我们可以确定当前的最大值就在当前顶部元素的左右子节点其中的一个。

代码实现:

@Test
    public void heapSort(){
        HeapSort hs = new HeapSort();
        int[] array = {16,7,3,20,17,8};
        System.out.print("构建大顶堆:");
        hs.toString(hs.buildMaxHeap(array));
        System.out.print("\n"+"大顶堆排序:\n");
        int[] result=hs.heapSort(array);
        System.out.print("\nresult;");
        hs.toString(result);
    }

//构建大根堆:将array看成完全二叉树的顺序存储结构
    public int[] buildMaxHeap(int[] array) {
        //从最后一个节点array.length-1的父节点(array.length-1-1)/2开始,直到根节点0,反复调整堆
        for (int i = (array.length - 2) / 2; i >= 0; i--) {
            adjustDownToUp(array, i, array.length);
        }
        return array;
    }

    //将元素array[k]自下往上逐步调整树形结构
    private void adjustDownToUp(int[] array, int k, int length) {
        int temp = array[k];
        int i = 2 * k + 1; //左子节点
        if (i < length - 1 && array[i] < array[i + 1]) { //当前是双子节点
            i++;
        }

        if (temp < array[i]) { //用大子节点的和父节点对比
            array[k] = array[i];
            array[i] = temp;

            //如果交换后子还有孙节点则可能不满足堆,再次进行筛选,当前的i就是k
            if (2 * i + 1 < length) adjustDownToUp(array, i, length);
        }

    }


    //堆排序
    public int[] heapSort(int[] array) {
        array = buildMaxHeap(array); //初始建堆,array[0]为第一趟值最大的元素
        for (int i = array.length - 1; i > 0; i--) {
            int temp = array[0];  //将堆顶元素和堆低元素交换,即得到当前最大元素正确的排序位置
            array[0] = array[i];
            array[i] = temp;
            System.out.print("第" + (array.length-i) + "趟\t\t");
            if (i > 1) adjustDownToUp(array, 0, i);  //整理,将剩余的元素整理成堆,如果只剩余1个数就不整理了
            toString(array);
            System.out.print("\n");
        }
        return array;
    }

    public void toString(int[] array) {
        for (int i : array) {
            System.out.print(i + "\t");
        }
    }

输出结果:


heap.png
7、插入排序

思想:
  把数组一个个取出来插入到一组有序的数组中去,每次插入的时候都要取寻找插入的位置,如果当前位置的数大于插入的数,则前移插入指针,直到当前位置小于或等于要插入的数,后移一位当前位置后面的数,把数插入当前位置后面留出来的位置即可。

8、希尔排序

思想:
  希尔排序是插入排序的一种改进

9、几种排序算法的时间空间复杂度分析
复杂度.png
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,569评论 4 363
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,499评论 1 294
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 109,271评论 0 244
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,087评论 0 209
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,474评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,670评论 1 222
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,911评论 2 313
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,636评论 0 202
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,397评论 1 246
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,607评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,093评论 1 261
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,418评论 2 254
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,074评论 3 237
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,092评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,865评论 0 196
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,726评论 2 276
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,627评论 2 270

推荐阅读更多精彩内容

  • 一:冒泡排序 冒泡排序(Bubble Sort)也是一种简单直观的排序算法。它重复地走访过要排序的数列,一次比较两...
    南方小小姑娘阅读 372评论 0 2
  • 概述 排序有内部排序和外部排序,内部排序是数据记录在内存中进行排序,而外部排序是因排序的数据很大,一次不能容纳全部...
    蚁前阅读 5,101评论 0 52
  • Java算法 - 排序算法 插入排序 思路简介 假定待排序数组长度为N,假设前n-1个数已经排好序,通过比较第n个...
    Sammy_ao阅读 660评论 0 9
  • 图片发自简书App 01 回忆就像未愈合的伤口一样,一碰就疼,看着还是觉得疼。如同我们的青春,疼痛的青春。 我的青...
    李珵美阅读 2,096评论 10 28
  • 昔日君妾在,各执龙凤箫。 龙箫声低沉,凤箫音灵巧。 雪竹风琳琅,江边答渔樵。 春江花月夜,相伴歌飘渺。 怨君不肯持...
    小沛卿阅读 312评论 1 1