排序算法

首先先上一个是十大排序算法对比图


image.png

1、冒泡排序(Bubble Sort)

算法流程:1、从头开始比较相邻的两个元素,如果第一个比第二个大,就交换他们的位置,执行完一轮后,就把最大的元素排到了最后一位
2、忽略最后一位,重新执行1,直到全部元素有序。
代码:

 protected void sort() {
        for (int end = array.length - 1; end > 0; end--) {
            int sortedIndex = 1;
            for (int begin = 1; begin <= end; begin++) {
                if (cmp(begin, begin - 1) < 0) {
                    swap(begin, begin-1);
                    sortedIndex = begin;
                }
            }
            end = sortedIndex;
        }

优化1,如果全部有序就提前结束循环
优化2,如果部分有序,后面那部分有序的前一个索引,减少交换遍历次数

2、选择排序(Selection Sort)

算法流程:
1、从序列中找出最大的那个元素,然后与最末尾的元素进行交换,执行完一轮后,最末尾的那个元素就是最大的元素
2、忽略1中找到的最大元素,重复执行1

private static void selectionSort(Integer[] array) {
        for (int end = array.length - 1; end > 0; end--) {

            // 默认0号元素为最大值
            int maxIndex = 0;
            for (int begin = 1; begin <= end; begin++) {
                // 为了保证排序的稳定性,要写>=
                if (array[begin] >= array[maxIndex]) {
                    maxIndex = begin;
                }
            }
            // 和最末尾的元素进行交换
            int temp = array[maxIndex];
            array[maxIndex] = array[end];
            array[end] = temp;
        }
        System.out.println(Arrays.toString(array));
    }

3、堆排序(Heap Sort)

堆排序可以认为是对选择排序的一种优化
算法流程:
1、对序列进行原地建堆(heapify)
2、重复执行以下操作,知道堆的元素数量为1
  (1)交换堆顶元素与尾元素
  (2)堆的元素数量减1
  (3)对0位置进行一次siftDown操作


image.png

image.png

image.png

image.png

image.png

image.png
public class HeapSort extends Sort{

    private int heapSize;
    @Override
    protected void sort() {
        // 原地建堆
        // 原地建堆
        heapSize = array.length;
        for (int i = (heapSize >> 1) - 1; i >= 0; i--) {
            siftDown(i);
        }
        while(heapSize > 1) {
            // 交换堆顶元素和尾部元素
            swap(0,--heapSize);

            // 对0位置进行siftDown,(恢复堆的性质)
            siftDown(0);

        }
        //System.out.println(Arrays.toString(array));
    }

    private void siftDown(int index) {
        Integer element = array[index];

        int half = heapSize >> 1;
        while (index < half) { // index必须是非叶子节点
            // 默认是左边跟父节点比
            int childIndex = (index << 1) + 1;
            Integer child = array[childIndex];

            int rightIndex = childIndex + 1;
            // 右子节点比左子节点大
            if (rightIndex < heapSize &&
                    cmpElement(array[rightIndex], child) > 0) {
                child = array[childIndex = rightIndex];
            }

            // 大于等于子节点
            if (cmpElement(element, child) >= 0) break;

            array[index] = child;
            index = childIndex;
        }
        array[index] = element;
    }
}

4、插入排序(Inserttion Sort)

插入排序非常类似于扑克牌的排序


image.png

算法流程:
1、执行过程中会将序列分成两部分,前面的部分是排好序的,后面的部分是待排序的
2、从头到尾扫描每一个元素,每当扫到一个元素,就把他插入到合适的位置,使得前面的部分依然是排好序的

 protected void sort() {
        for (int begin = 1; begin < array.length; begin++) {
            int  cur = begin;
            while (cur > 0 && cmp(cur, cur - 1) < 0) {
                swap(cur, cur-1);
                cur--;
            }
        }
        //System.out.println(Arrays.toString(array));
    }

插入排序优化1,将交换变为挪动
1、先将待插入的元素备份
2、头部有序数据中比待插入元素大,都朝尾部方向挪动一个位置
3、将待插入元素放到最终合适的位置


image.png
  // 优化
    protected void sort1() {
        for (int begin = 1; begin < array.length; begin++) {
            int cur = begin;
            Integer v = array[cur];

            while(cur > 0 && cmp(cur, cur - 1) < 0) {
                array[cur] = array[cur - 1];
                cur--;
            }
            array[cur] = v;
        }
    }

插入排序优化2
先来说一下二分搜索(Binary Search)


image.png

  二分搜索思路:
1、假设在[begin,end}的范围内搜索一个元素v,先找到位置中点的元素mid=array[(begin+end)>>1;]
2、如果v大于mid,则排除前半部分,begin=midIndex+1,重复执行1
3、如果v小于mid,则排除半部分,end = midIndx,重复执行1
4、如果v等于mid,直接返回mid


image.png
image.png

image.png
public  int indexOf(Integer[] array, int v) {
        if (array == null || array.length == 0) {
            return  -1;
        }
        int begin = 0;
        int end = array.length;

        while(begin < end) {
            int mid = (begin + end) >> 1;
            if (array[mid] < v) {
                begin = mid + 1;
            } else if (array[mid] > v) {
                end = mid;
            } else {
                return mid;
            }
        }
        return -1;

    }

插入查找优化代码

// 优化
    protected void sort2() {
        for (int begin = 1; begin < array.length; begin++) {
            Integer element = array[begin];
            int insertIndex = search(begin);
            for (int i = begin ; i > insertIndex ; i--) {
                array[i] = array[i -1];
            }
            array[insertIndex] = element;
        }
    }
    private  int search(int index) {
        int v = array[index];
        int begin = 0;
        int end = index;
        while (begin < end) {
            int mid = (begin + end) >> 1;
            if (v >= array[mid]) {
                begin = mid + 1;
            } else {
                end = mid;
            }
        }
        return begin;
    }

5、归并排序

算法流程:
1、不断地将当前的序列平均分割成2个子序列
  直到不能再分割(序列中只剩一个元素)
2、不断将2个子序列合并成一个有序序列
  直到最终只剩下1个有序序列
图示


image.png

分割代码:

protected void sort() {
        leftArray =  new Integer[array.length >> 1];
        sort(0, array.length);
        System.out.println(Arrays.toString(array));
    }
    
    // T(n) = T(n/2) + T(n/2) + O(n)
    
    /**
     * 对 [begin, end) 范围的数据进行归并排序
     */
    private void sort(int begin, int end) {
        if (end - begin < 2) return;
        
        int mid = (begin + end) >> 1;
        sort(begin, mid);
        sort(mid, end);
        merge(begin, mid, end);
    }

归并merge细节


image.png

image.png

image.png

image.png

image.png

代码:

package dierjisuanfa.sort;


import java.util.Arrays;

public class MergeSort extends Sort {
    private Integer[] leftArray;

    @Override
    protected void sort() {
        leftArray =  new Integer[array.length >> 1];
        sort(0, array.length);
        System.out.println(Arrays.toString(array));
    }
    
    // T(n) = T(n/2) + T(n/2) + O(n)
    
    /**
     * 对 [begin, end) 范围的数据进行归并排序
     */
    private void sort(int begin, int end) {
        if (end - begin < 2) return;
        
        int mid = (begin + end) >> 1;
        sort(begin, mid);
        sort(mid, end);
        merge(begin, mid, end);
    }
    
    /**
     * 将 [begin, mid) 和 [mid, end) 范围的序列合并成一个有序序列
     */
    private void merge(int begin, int mid, int end) {
        int li = 0, le = mid - begin;
        int ri = mid, re = end;
        int ai = begin;
        
        // 备份左边数组
        for (int i = li; i < le; i++) {
            leftArray[i] = array[begin + i];
        }
        
        // 如果左边还没有结束
        while (li < le) {

                if (ri < re && array[ri]<leftArray[li]) {
                    array[ai++] = array[ri++];
                } else {
                    array[ai++] = leftArray[li++];
                }
            }

    }
}

6、快速排序

算法流程:
1、从序列中选择一个轴点元素(pivot)
  假设每次选择0位置为轴点元素
2、利用pivot将序列分割成两个子序列
  将小于pivot的元素放在pivot左侧
  将大于pivot的元素放在pivot右侧
  等于pivot的元素放在哪边都可以
3、对子序列进行1,2操作
  直到不能再分割(子序列中只剩下1个元素)
图示


image.png

image.png

image.png

代码:

package dierjisuanfa.sort;

import java.util.Arrays;

/**
 * 快速排序
 * 1.从序列中选择一个轴点元素
 *      假设每次选择0位置的元素为轴点元素
 *
 * 2.利用pivot将序列分割为2个子序列
 *      将小于pivot的元素放在pivot前面(左侧)
 *      将大于pivot的元素放在pivot后面(右侧)
 * 3.对子序列进行1,2操作
 *      直到不能再分割(子序列只剩一个元素)
 *
 * 快速排序的本质
 * 逐渐将每一个元素都转换成轴点元素
 */
public class QuickSort extends Sort{
    @Override
    protected void sort() {
        sort(0, array.length);
        System.out.println(Arrays.toString(array));
    }

    /**
     * 对[begin, end)范围的元素进行快速排序
     * @param beign
     * @param end
     */
    private void sort(int beign, int end){
        if(end - beign < 2) {
            return;
        }

        // 确定轴点位置
        int mid = pivotIndex(beign, end);
        // 对子序列也进行快速排序
        sort(beign, mid);
        sort(mid + 1, end);
    }

    /**
     * 确定[begin,end)范围的轴点元素
     * @param beign
     * @param end
     * @return 轴点元素的最终位置
     */
    private int pivotIndex(int beign, int end) {
        // 随机选择一个元素跟begin位置进行交换
        swap(beign,beign + (int)(Math.random()*(end-beign)));
        // 备份begin位置的元素
        Integer pivot = array[beign];
        end--;

        while(beign < end) {
            while(beign < end) {
                // 从右往左扫描
                if(cmpElement(pivot,array[end]) < 0) {// 右边元素 > 轴点元素
                    end--;
                } else {
                    array[beign] = array[end];
                    beign++;
                    break;
                }
            }
            while(beign < end) {
                // 从左往右扫描
                if (cmpElement(pivot, array[beign]) > 0) {
                    beign++;
                } else {
                    array[end] = array[beign];
                    end--;
                    break;
                }
            }


        }
        // 将轴点元素放入最终位置
        array[beign] = pivot;
        // 返回轴点元素的位置
        return beign;
    }
}

7、希尔排序

希尔排序把序列看作是一个矩阵,分为m列,逐列进行排序
m从某个整数逐渐减为1
当m为1是,整个序列将完全有序

希尔排序也被称为递减增量排序

矩阵的列数取决于步长序列
比如,如果步长序列为{1,5,19,41,109},就代表依次分为109列,41列,19列,5列,1列进行排序
不同的步长序列,执行效率也不同


image.png

image.png

image.png

image.png

image.png

代码:



    @Override
    protected void sort() {
        List<Integer> stepSequence = shellStepSequence();
        System.out.println(stepSequence);
        for (Integer step : stepSequence) {
            sort(step);
        }
        System.out.println(Arrays.toString(array));
    }
    private void sort(int step) {
        // col:第几列,column的简称
        for (int col = 0; col < step; col++) {
            // 对[0,array.length)排序
            for (int begin = col+step; begin < array.length; begin+=step) {
                int  cur = begin;
                while (cur > col && cmp(cur, cur - step) < 0) {
                    swap(cur, cur-step);
                    cur-=step;
                }
            }
        }
    }

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

推荐阅读更多精彩内容