zoukankan      html  css  js  c++  java
  • 基本排序算法(冒泡排序 选择排序 插入排序 快速排序 归并排序 基数排序 希尔排序)

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

    原理:

    比较相邻的元素。如果第一个比第二个大,就交换他们两个。
    每一轮对每一对相邻元素做同样的工作,从开始第一对到结尾的最后一对。第一轮结束,最后的元素应该会是最大的数。
    针对所有的元素重复以上的步骤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; //每次步长处理
    }
    }
  • 相关阅读:
    纪念我用word发布的第一篇文章
    第一个SpringMVCHelloWorld
    JSTL学习笔记
    bonecp的使用
    hdu 1556 树状数组
    hdu 1561 树形DP
    MYSQL使用笔记
    Android中简单实现Spinner的数据绑定
    Android中利用Application实现多个Activity间共享数据
    技术到底重要不重要?
  • 原文地址:https://www.cnblogs.com/itchenguo/p/10677837.html
Copyright © 2011-2022 走看看