基本排序算法(冒泡排序 选择排序 插入排序 快速排序 归并排序 基数排序 希尔排序)

冒泡排序:两两比较,大数冒泡

原理:

比较相邻的元素。如果第一个比第二个大,就交换他们两个。
每一轮对每一对相邻元素做同样的工作,从开始第一对到结尾的最后一对。第一轮结束,最后的元素应该会是最大的数。
针对所有的元素重复以上的步骤n-1轮,分别倒序排好倒数第二大、倒数第三大……的元素。直到没有任何一对数字需要比较。
形象排序:

原始数据: 6 2 8 5 1
第一轮: 2 6 5 1 |8
第二轮: 2 5 1 |6 8
第三轮: 2 1 |5 6 8
第四轮: 1 |2 5 6 8
1
2
3
4
5
具体解释:

第一轮中:
第一次:比较第一个和第二个元素(即6和2),发现6比2大,则交换6和2(目前数字排列为 2 6 8 5 1)
第二次:比较第二个和第三个元素(即6和8),发现6比8小,则保持原样(目前数字排列为 2 6 8 5 1)
第三次:比较第三个和第四个元素(即8和5),发现8比5大,则交换8和5(目前数字排列为 2 6 5 8 1)
第四次:比较第四个和第五个元素(即8和1),发现8比1大,则交换8和1(目前数字排列为 2 6 5 1 8)
最后:这样,第一轮就把最大的元素放到了最右边

最后一个元素已经有序,则第二轮不用比较最后一个元素和倒数第二个元素的大小(目前数字排列为 2 6 5 1 |8)
第二轮中:
第一次:比较第一个和第二个元素(即2和6),发现2比6小,则保持原样(目前数字排列为 2 6 5 1 |8)
第二次:比较第二个和第三个元素(即6和5),发现6比5大,则交换6和5(目前数字排列为 2 5 6 1 |8)
第三次:比较第三个和第四个元素(即6和1),发现6比1大,则交换6和1(目前数字排列为 2 5 1 6 |8)
最后:这样,第二轮就把倒数第二大的元素放到了倒数第二的位置

倒数两个元素已经有序,则第三轮不用比较倒数第三个元素和倒数第二个元素的大小(目前数字排列为2 5 1 |6 8)
第三轮中:
第一次:比较第一个和第二个元素(即2和5),发现2比5小,则保持原样(目前数字排列为 2 5 1 |6 8)
第二次:比较第二个和第三个元素(即5和1),发现5比1大,则交换5和1(目前数字排列为 2 1 5 |6 8)
最后:这样,第三轮就把倒数第三大的元素放到了倒数第三的位置

倒数三个元素已经有序,则第四轮不用比较倒数第四个元素和倒数第三个元素的大小(目前数字排列为2 1 |5 6 8)
第四轮中:
第一次:比较第一个和第二个元素(即2和1),发现2比1大,则交换2和1(目前数字排列为 1 2 |5 6 8)
最后:这样,第四轮就把倒数第四大的元素放到了倒数第四的位置,所有的元素就按升序排好了

升序:

复制代码
public static void bubbleSort(int[] arr){
        int lgn = arr.length;
        for (int i = 0; i < lgn - 1; i++) {
            for (int j = 0; j < lgn - 1 - i; j++) {
                if(arr[j] > arr[j + 1]){
                    int temp = arr[j + 1];
                    arr[j + 1] = arr[j];
                    arr[j] = temp;
                }
            }
        }
    }
复制代码

降序:

...

选择排序:选择剩余元素中最小(最大)的元素放置到初始选择集合中(空)

1. 选择排序法
(如果不想看解释分析,直接往后拉看代码)

实质:

第一轮:通过对比数组中前一个元素和后一个元素的大小,先找到数组中最小的数字,并且记录它的下标。如果标记的下标不是第一个元素的下标,则交换两个元素。

第二轮:从第二个元素开始(因为第一个元素已经是第一轮排好的“有序”元素),对比数组中前一个元素和后一个元素的大小,遍历寻找数组中第二小的数字,并记录它的下标。如果标记的下标不是第二个元素的下标,则交换两个元素。

第三轮:从第三个元素开始(因为第一和第二个元素已经是第一、二轮排好的“有序”元素),对比数组中前一个元素和后一个元素的大小,遍历寻找数组中第三小的数字,并记录它的下标。如果标记的下标不是第三个元素的下标,则交换两个元素。

第四轮:从第四个元素开始(因为第一、第二、第三个元素已经是第一、二、三轮排好的“有序”元素),对比数组中前一个元素和后一个元素的大小,遍历寻找数组中第四小的数字,并记录它的下标。如果标记的下标不是第四个元素的下标,则交换两个元素。

第五轮:没有第五轮(因为一共就五个数,排好四个数,自然就全部排好了,再多排一轮浪费了)

形象排序:

原始数据: |6 2 8 5 1
第一轮: 1 |2 8 5 6
第二轮: 1 2 |8 5 6
第三轮: 1 2 5 |8 6
第四轮: 1 2 5 6 |8
1
2
3
4
5
具体解释:
先假设第一个数字6为最小的数字,那么记录它的下标(index=0)

第一轮中:
第一次:先比较6和2(即标记的数和后面一个元素比较),发现2更小,则记录2的下标(index=1)
第二次:比较2和8(标记的数和后面一个元素比较),发现还是2小,则下标还是2的下标不变(index=1)
第三次:比较2和5(标记的数和更后面的数比较),发现还是2小,则下标还是2的下标不变(index=1)
第四次:比较2和1(标记的数和更后面的数比较),发现1比2小,则标记改为1的下标(index=4)
最后:index并不等于第一个元素的下标(0),则交换第一个元素和被标记的元素

第一个元素已经有序,则从第二个元素开始(并且把标记index初始化为1,代表第二个元素2)
第二轮中:
第一次:先比较2和8,还是2小,则下标还是2的下标不变(index=1)
第二次:比较2和5,还是2小,则下标还是2的下标不变(index=1)
第三次:比较2和6,还是2小,则下标还是2的下标不变(index=1)
最后:index等于第二个元素的下标(1),则不用交换第二个元素和被标记的元素

第一、二个元素(1和2)已经有序,则从第三个元素开始(并且把标记index初始化为2,代表第三个元素8)
第三轮中:
第一次:先比较8和5,发现5比8小,则标记改为5的下标(index=3)
第二次:比较5和6,还是5小,则下标还是5的下标不变(index=3)
最后:index并不等于第三个元素的下标(2),则交换第三个元素和被标记的元素

第一、二、三个元素(1和2和5)已经有序,则从第四个元素开始(并且把标记index初始化为3,代表第四个元素8)
第四轮中:
第一次:先比较8和6,发现6比8小,则标记改为6的下标(index=4)
最后:index并不等于第四个元素的下标(3),则交换第四个元素和被标记的元素

复制代码
public static void SelectionSortAsc(int[] arr){
int min = 0;
for (int i = 0; i < arr.length - 1; i++) {
min = i;
for (int j = i + 1; j < arr.length ; j++) {
if(arr[j] < arr[min]){
min = j;
}
}
if(min != i){
int tmp = arr[i];
arr[i] = arr[min];
arr[min] = tmp;
}
}
}

public static void SelectionSortDesc(int[] arr){
int max = 0;
for (int i = 0; i < arr.length - 1; i++) {
max = i;
for (int j = i + 1; j < arr.length ; j++) {
if(arr[j] > arr[max]){
max = j;
}
}
if(max != i){
int tmp = arr[i];
arr[i] = arr[max];
arr[max] = tmp;
}
}
}
复制代码

插入排序:设定一个初始已排序的集合(一般选择一个元素),从剩余的集合中将各个元素以此插入到初始集合中的正确位置

原理:
插入排序法通过把数组中的元素插入到适当的位置来进行排序:

先假设第一个元素有序,则第一轮从第二个元素开始,作为待插入的数,往前依次比较,看往哪里插
第二轮把下一个元素(第三个)插入到其对应于已排序元素的排序位置
对于数组中的每个元素重复2步骤。即把第四个元素插入到适当位置,然后是第5个元素,等等。
形象排序:

原始数据: 6| 2 8 5 1
第一轮: 2 6| 8 5 1
第二轮: 2 6 8| 5 1
第三轮: 2 5 6 8| 1
第四轮: 1 2 5 6 8|
1
2
3
4
5
具体解释:

假设第一个元素6是有序的,并且定义待插入的数int inserter=a[i],和定义下标index=i-1,用此下标来让插入点与对应数比较

因为第一个数假设是有序的,则从第二个数开始作为待插入的数(inserter=a[1])
第一轮中:
第一次:把inserter与第一个元素比较(即2与6),发现2比6小,则把第一个元素后挪一个位置(目前数字排列为 6 6| 8 5 1)
最后:把inserter中保留的待插入的数插入到相应位置(目前数字排列为 2 6| 8 5 1)

前面两个元素已经有序,则第二轮把第三个元素插到有序元素中的适当位置,则实现前三个元素有序(目前数字排列为 2 6| 8 5 1)
第二轮中:(保存第三个元素inserter=a[2])
第一次:把inserter与第二个元素比较(即8与6),发现8比6大,则把第二个元素不做后挪(目前数字排列为 2 6 8| 5 1)
第二次:由于8比6大,所以肯定比2大,所以不需要再比了
最后:把inserter中保留的待插入的数插入到相应位置(对于本题,则还是原位置)(目前数字排列为 2 6 8| 5 1)

前面三个元素已经有序,则第三轮把第四个元素插到有序元素中的适当位置,则实现前四个元素有序(目前数字排列为 2 6 8| 5 1)
第三轮中:(保存第四个元素inserter=a[3])
第一次:把inserter与第三个元素比较(即5与8),发现5比8小,则把第三个元素后挪一个位置(目前数字排列为 2 6 8 8| 1)
第二次:把inserter与第二个元素比较(即5与6),发现5比6小,则把第二个元素后挪一个位置(目前数字排列为 2 6 6 8| 1)
第三次:把inserter与第一个元素比较(即5与2),发现5比2大,则把第一个元素不做后挪(目前数字排列为 2 6 6 8| 1)
最后:把inserter中保留的待插入的数插入到相应位置(目前数字排列为 2 5 6 8| 1)

前面四个元素已经有序,则第四轮把第五个元素插到有序元素中的适当位置,则实现前五个元素有序(目前数字排列为 2 5 6 8| 1)
第五轮中:(保存第五个元素inserter=a[4])
第一次:把inserter与第四个元素比较(即1与8),发现1比8小,则把第四个元素后挪一个位置(目前数字排列为 2 5 6 8 8|)
第二次:把inserter与第三个元素比较(即1与6),发现1比6小,则把第三个元素后挪一个位置(目前数字排列为 2 5 6 6 8|)
第三次:把inserter与第二个元素比较(即1与5),发现1比5小,则把第二个元素后挪一个位置(目前数字排列为 2 5 5 6 8|)
第四次:把inserter与第一个元素比较(即1与2),发现1比2小,则把第一个元素后挪一个位置(目前数字排列为 2 2 5 6 8|)
最后:把inserter中保留的待插入的数插入到相应位置(目前数字排列为 1 2 5 6 8|),完成排序

复制代码
public static void insertionSort(int [] array){
        for(int i = 1; i < array.length; i++){
            int temp = array[i];//被标记的值或者说是当前需要插入的值
            int j = i;
            //如果轮循值大于被标记值则往后移
            while( j > 0 && temp < array[j - 1]){
                array[j] = array[j - 1];
                j-- ;
            }
            //将被标记值插入最终移出的空位置
            array[j] = temp;
        }
    }
复制代码

快速排序:选取锚点,划分小于,大于两个集合,递归处理子集合

原理:
通过一趟排序将要排序的数据分割成独立的两部分,其中一部分的所有数据都比另外一部分的所有数据都要小,然后再按此方法对这两部分数据分别进行快速排序,整个排序过程可以递归进行,以此达到整个数据变成有序序列。

先假设第一个元素为轴值,自右向左找一个比轴值小的数交换,再自左向右找一个比轴值大的数交换,再重复自右向左找一个比轴值小的数交换,自左向右找一个比轴值大的数交换,直到轴值左边没有比其大的数存在,右边也没有比其小的数存在,则第一轮结束。原来的一组数据被划分为以轴值为界的两组新数据
第二轮:取上一轮轴值左边的一组新数据,重复1的操作;取上一轮轴值右边的一组新数据,重复1的操作,则把最初的一组数据分成了四部分,这样便产生一个递归的思想
一直重复操作,直到数据被分的不可再分为止。
形象排序:

原始数据: |6| 2 8 5 1
第一轮: |1| 2 5 | 6 |8|
第二轮: 1 ||2| 5 | 6 | 8
第三轮: 1 | 2 | 5 | 6 | 8
1
2
3
4
具体解释:
第一轮中:(先假设第一个元素6为轴值)
第一次:自右向左找一个比轴值(即6)小的数交换,正巧右边的第一个数就比6小,则交换6和1(目前数字排列为 1 2 8 5 6)
第二次:自左向右找一个比轴值(即6)大的数交换,左边第一个数为1,不比6大,则找左边第二个数;左边第二个数为2,不必6大,找左边第三个数;左边第三个数为8,比6大,则交换6和8(目前数字排列为 1 2 6 5 8)
第三次:再自右向左找一个比轴值(即6)小的数交换,右边第一个数为8,不比6小,则找右边第二个数;右边第二个数为5,比6小,则交换6和5(目前数字排列为 1 2 5 6 8)
第四次:再自左向右找一个比轴值(即6)大的数交换,左边第一个数为1,不比6大,则找左边第二个数;左边第二个数为2,不必6大,则找左边第三个数;左边第三个数为5,不比6大,则找左边第四个数,结果第四个书就是轴值本身,则一轮循环停止(目前数字排列为 1 2 5 6 8)
最后:这样,第一轮就把最初的一组元素{ 6 2 8 5 1 }分为两组元素{ 1 2 5 }和{ 8 }(6为轴值,经历这几次遍历,便已经固定其正确位置了,以后不需要再考虑这个元素)

第二轮中:
先考虑第一轮轴值(即6)左边的数据 { 1 2 5 }:
第二轮中左边新数据的第一轮:(先假设新数据的第一个元素1为新的轴值)自右向左找一个比轴值(即1)小的数交换,右边第一个数为5,不比1小,则找右边第二个数;右边第二个数为2,不比1小,则找右边第三个数,结果右边第三个数就是轴值本身,则循环停止(目前数字排列为 1 2 5 ),同样的循环已经固定轴值(即1)的位置
同时,轴值1的左边没有数据,即分到了不可再分的地步,那么递归结束,而轴值1的右边还有数据 { 2 5 },则继续确立新的轴值为2,再进行如上操作,直到分到不可以再分,则递归终止,最后可以确保第一轮的轴值(即6)左边的新数据 { 1 2 5 }每个都被固定,则左边数据的递归结束。
再考虑第一轮轴值(即6)右边的数据 { 8 }:
已经被分到不可再分,则它的位置就已经被确定了,右边数据的递归结束。
最终整组数据就排列完毕

复制代码
public static void quikeSort(int[] arr, int low, int high) {
        int start = low;
        int end = high;
        int anchor = arr[low];

        while (end > start) {
            //比较 anchor---end
            while (end > start && arr[end] >= anchor) {  //从末尾向前寻找小于等于anchor的值
                end--;
            }

            //交换位置
            if (arr[end] <= anchor) {
                int temp = arr[end];
                arr[end] = arr[start];
                arr[start] = temp;
            }

            //比较start---anchor
            while (end > start && arr[start] <= anchor) {//从起始位置向后寻找大于等于anchor的值
                start++;
            }

            //交换位置
            if (arr[start] >= anchor) {
                int temp = arr[start];
                arr[start] = arr[end];
                arr[end] = temp;
            }
        }
        //anchor位置确定。左边的元素值都小于anchor值,右边的值都大于anchor值,递归排序左右两侧排序
        //左边元素。low---anchor值索引-1
        if (start > low) {
            quikeSort(arr, low, start - 1);
        }

        //右边元素。从anchor值索引+1---high
        if (end < high) {
            quikeSort(arr, end + 1, high);
        }
    }
复制代码

归并排序:中分两个结合,分别归并排序,然后合并两个有序结合;递归进行

复制代码
public static void mergeSort(int[] data) {
        sort(data, 0, data.length - 1);
    }

    public static void sort(int[] data, int left, int right) {
        if (left >= right)
            return;
        // 找出中间索引
        int center = (left + right) / 2;
        // 对左边数组进行递归
        sort(data, left, center);
        // 对右边数组进行递归
        sort(data, center + 1, right);
        // 合并
        merge(data, left, center, right);
    }

    /**
     * 将两个数组进行归并,归并前面2个数组已有序,归并后依然有序
     *
     * @param data
     *            数组对象
     * @param left
     *            左数组的第一个元素的索引
     * @param center
     *            左数组的最后一个元素的索引,center+1是右数组第一个元素的索引
     * @param right
     *            右数组最后一个元素的索引
     */
    public static void merge(int[] data, int left, int center, int right) {
        // 临时数组
        int[] tmpArr = new int[data.length];
        // 右数组第一个元素索引
        int mid = center + 1;
        // third 记录临时数组的索引
        int third = left;
        // 缓存左数组第一个元素的索引
        int tmp = left;
        while (left <= center && mid <= right) {
            // 从两个数组中取出最小的放入临时数组
            if (data[left] <= data[mid]) {
                tmpArr[third++] = data[left++];
            } else {
                tmpArr[third++] = data[mid++];
            }
        }
        // 剩余部分依次放入临时数组(实际上两个while只会执行其中一个)
        while (mid <= right) {
            tmpArr[third++] = data[mid++];
        }
        while (left <= center) {
            tmpArr[third++] = data[left++];
        }
        // 将临时数组中的内容拷贝回原数组中
        // (原left-right范围的内容被复制回原数组)
        while (tmp <= right) {
            data[tmp] = tmpArr[tmp++];
        }
    }
复制代码

基数排序:逐位排序

复制代码
//LSD
    public static void radixLSDSort(int[] arr){
        //最高位数
        int maxBit = getMaxBit(arr);
        //十个bulk 分别存放 每个位数 数字 相应的元素 如个位为3 则放入bulk[3]
        Queue<Integer>[] bulk = new Queue[10];
        for (int i = 0; i < bulk.length; i++) {
            bulk[i] = new ArrayDeque();
        }
        //分别处理不同位数 存放 处理
        for (int i = 0; i < maxBit; i++) {
            for (int j = 0; j < arr.length; j++) {
                int ivalue = (int) (arr[j] % Math.pow(10, i + 1) / Math.pow(10, i)); //去相应位数上的数字作为 存入bulk index
                bulk[ivalue].offer(arr[j]);
            }

            //取出bulk中的元素 重新放入数组 并清除bulk中的元素。
            int arrIndex = 0;
            for (int j = 0; j < bulk.length; j++) {
                while (bulk[j].size()>0){
                    arr[arrIndex++] = bulk[j].poll();
                }
            }
        }
    }

    public static int getMaxBit(int[] arr){
        int max = arr[0];
        for (int i = 1; i < arr.length; i++) {
            if(arr[i] > max){
                max = arr[i];
            }
        }

        int b = 0;
        while (max > 0){
            max /= 10;
            b++;
        }
        return b;
    }

    public static void radixMSDSort(int[] arr){
        msdSort(arr, 0, arr.length, getMaxBit(arr));
    }

    //MSD
    public static void msdSort(int[] arr, int left, int right, int maxBit){
        //十个bulk 分别存放 每个位数 数字 相应的元素 如个位为3 则放入bulk[3]
        Queue<Integer>[] bulk = new Queue[10];
        for (int i = 0; i < bulk.length; i++) {
            bulk[i] = new ArrayDeque();
        }
        //依据最高位分别放入不同的bulk
        for (int j = left; j < right; j++) {
            int ivalue = (int) (arr[j] % Math.pow(10, maxBit) / Math.pow(10, maxBit - 1)); //去相应位数上的数字作为 存入bulk index
            bulk[ivalue].offer(arr[j]);
        }

        //取出bulk中的元素 重新放入数组 并清除bulk中的元素。记录bulk中queue大小大于1的数组索引 递归使用
        List<Integer> count = new ArrayList<Integer>();
        int arrIndex = left;
        for (int j = 0; j < bulk.length; j++) {
            int start = arrIndex;
            while (bulk[j].size()>0){
                arr[arrIndex++] = bulk[j].poll();
            }
            if(arrIndex - start > 1){
                count.add(start);
                count.add(arrIndex);
            }
        }
        //递归最小位判断
        int nextBit = maxBit - 1;
        if(nextBit > 0) {
            for (int i = 1; i < count.size(); i += 2) {
                msdSort(arr, count.get(i - 1), count.get(i), nextBit);
            }
        }
    }
复制代码

shell排序:插入排序+步长改进

复制代码
public static void shellSort(int[] arr){
        int step = arr.length/2;
        while (step >= 1) { //步长为1时 包含所有数组序列
            for (int i = 0; i < step; i++) { //步长为n则数组将分为n组分别排序
                for (int j = step + i; j < arr.length; j += step) { //对跨步长每组元素进行插入排序
                    int temp = arr[j];
                    int preIndex = j - step;
                    while (preIndex > -1 && temp < arr[preIndex]) {
                        arr[preIndex + step] = arr[preIndex];
                        preIndex -= step;
                    }
                    arr[preIndex + step] = temp;
                    System.out.println(" middle: " + Arrays.toString(arr));
                }
            }
            step /= 2; //每次步长处理
        }
    }
 
/**
* 改进形式
* @param arr
*/
public static void shellSort1(int[] arr){
int step = arr.length/2;
while (step >= 1) { //步长为1时 包含所有数组序列
for (int i = step; i < arr.length; i += step) { //对跨步长每组元素进行插入排序
int temp = arr[i];
int j = i;
while (j > 0 && temp < arr[j - step]) {
arr[j] = arr[j - step];
j -= step;
}
arr[j] = temp;
}
System.out.println("step: " + step + " middle: " + Arrays.toString(arr));
step /= 2; //每次步长处理
}
}
原文地址:https://www.cnblogs.com/itchenguo/p/10677837.html