数据结构与算法之经典排序

来源:互联网 发布:au录音软件win10 编辑:程序博客网 时间:2024/05/15 12:10

1 经典算法对比

这里写图片描述

这里写图片描述

2 衡量算法的优劣指标

(1)时间复杂度:主要是分析关键字的比较次数和记录的移动次数。

(2)空间复杂度:分析排序算法中需要多少辅助内存

(3)稳定性:若两个记录A和B的关键字值相等,但排序后A、B的先后次序保持不变,则称这种算法是稳定的;反之,就是不稳定的。

2 参考链接

java的几种经典排序算法

Java程序员必须掌握的8大排序算法

3 算法分类解析

3.1 直接插入排序

3.1.1 核心思想

核心思想:对n个数据要进行n-1趟比较,每趟比较 a[i] 与 a[i-1]–a[0] ,目的就是将大于temp的往后移动一位,并将temp插入进来本趟中的最小数据的位置。

3.1.2 演示图

这里写图片描述

3.1.3 源码

package com.dn.sort;public class InsertSort {    /**     * 直接插入排序     *      */    private static int[] insertSort(int[] a) {        for (int i = 1; i < a.length; i++) {             // 1.待插入元素            int temp = a[i];            int j;            for (j = i - 1; j >= 0; j--) {//j=i-1开始,j--,直至j=0或a[j] <= temp                // 2.将大于temp的往后移动一位                if (a[j] > temp) {                    a[j + 1] = a[j];                } else {                    break;// 执行后j--,所以插入时j+1                }            }            a[j + 1] = temp;// 3.temp插入进来本趟中的最小数据的位置        }        return a;    }    public static void main(String[] args) {        int[] a = { 49, 38, 65, 97, 76, 13, 27, 49, 78, 34, 12, 64, 1 };        System.out.println("排序之前:");        for (int i = 0; i < a.length; i++) {            System.out.print(a[i] + " ");        }        System.out.println();        System.out.println("排序之后:");        a = insertSort(a);        for (int i = 0; i < a.length; i++) {            System.out.print(a[i] + " ");        }    }}

3.2 二分法插入排序(折半插入排序)

3.2.1 核心思想

具体操作为:在将一个新元素temp插入已排好序的数组的过程中,寻找插入点时, 将待插入区域的首元素设置为a[left],末元素设置为a[high],比较时将temp与a[mid],其中mid=(left+high)/2相比较;
* 如果比参考元素小,则选择a[left]到a[m-1]为新的插入区域(即high=m-1);否则选择a[m+1]到a[high]为新的插入区域(即left=m+1),
* 如此直至left<=high不成立,即将此位置之后所有元素后移一位,并将新元素插入a[left]。

3.2.2 源码

package com.dn.sort;public class BinaryInsertSort {    /**     * 二分法插入排序(折半插入排序)     *      * 二分法插入排序是对直接插入排序的改进,     */    private static int[] binaryInsertSort(int[] a) {        for (int i = 0; i < a.length; i++) {            int temp = a[i];//待插入元素            int left = 0;            int right = i - 1;            int mid = 0;            // 确定要插入的位置            while (left <= right) {                // 找出left和high中间的索引                  mid = (left + right) / 2;                if (temp < a[mid]) {                    // 如果值比中间值小,限制在大于mid的那一半搜索,让left左移到"中间下标-1"                    right = mid - 1;                } else {                    // 如果值比中间值大,限制在小于mid的那一半搜索,让left右移到"中间下标+1"                    left = mid + 1;                }            }            //将left到i处的所有元素后移一位              for (int j = i - 1; j >= left; j--) {                a[j + 1] = a[j];            }            //元素位置变化时            if (left != i) {                // 元素位置变化时插入新元素temp(因为插入都在mid的右边mid+1,即是)                a[left] = temp;            }        }        return a;    }    public static void main(String[] args) {        int[] a = { 49, 38, 65, 97, 176, 213, 227, 49, 78, 34, 12, 164, 11, 18,                1 };        System.out.println("排序之前:");        for (int i = 0; i < a.length; i++) {            System.out.print(a[i] + " ");        }        // 二分插入排序        a = binaryInsertSort(a);        System.out.println();        System.out.println("排序之后:");        for (int i = 0; i < a.length; i++) {            System.out.print(a[i] + " ");        }    }}

3.3 希尔排序(缩小增量排序)

3.3.1 核心思想

核心思想:算法先将要排序的一组数按某个增量d(n/2,n为要排序数的个数)分成若干组,每组中记录的下标相差d.对每组中全部元素进行直接插入排序,然后再用一个较小的增量(d/2)对它进行分组,在每组中再进行直接插入排序。当增量减到1时,进行直接插入排序后,排序完成。

3.3.2 演示图

这里写图片描述

3.3.3 源码

package com.dn.sort;//不稳定public class HeerSort {    /**     * 希尔排序(缩小增量排序)     */    private static int[] heerSort(int[] a) {        // 希尔排序        System.out.println();        int d = a.length / 2;//默认增量        while (true) {            for (int i = 0; i < d; i++) {                for (int j = i; j + d < a.length; j += d) {//i开始,d为增量分组比较(eg:d=3; i=0; j=0、3,3、6,6、9,9、12,12、15)                    int temp;                    if (a[j] > a[j + d]) {//互换位置                        temp = a[j];                        a[j] = a[j + d];                        a[j + d] = temp;                    }                }            }            if (d == 1) {                break;            }            d--;//增量-1继续比较        }        return a;    }    /**     * 增强希尔排序&冒泡排序     *      */    private static int[] heerBubbleSort(int[] a) {        // 希尔排序        System.out.println();        int d = a.length / 2;        while (true) {            d = d/2;            for (int i = 0; i < d; i++) {                for (int j = i; j + d < a.length; j += d) {//i开始,d为增量分组比较并组内排序(eg:d=3; i=0; j=0、3、6、9、12、15)                    for (int n = i; n + d < a.length; n += d) {//冒泡排序(组内排序)                        int temp;                        if (a[n] > a[n + d]) {//互换位置                            temp = a[n];                            a[n] = a[n + d];                            a[n + d] = temp;                        }                    }                }            }            if (d == 1) {                break;            }            d--;//增量-1继续比较        }        return a;    }    // 希尔排序    public static void main(String[] args) {        int[] a = { 49, 38, 65, 97, 76, 13, 27, 49, 78, 34, 12, 64, 1, 33, 85,29 };        System.out.println("排序之前:");        for (int i = 0; i < a.length; i++) {            System.out.print(a[i] + " ");        }        a = heerBubbleSort(a);        System.out.println();        System.out.println("排序之后:");        for (int i = 0; i < a.length; i++) {            System.out.print(a[i] + " ");        }    }}

3.4 简单选择排序

3.4.1 核心思想

核心思想:在要排序的一组数中,选出最小的一个数与第一个位置的数交换;然后在剩下的数当中再找最小的与第二个位置的数交换,如此循环到倒数第二个数和最后一个数比较为止。

3.4.2 演示图

这里写图片描述

3.4.3 源码

package com.dn.sort;public class SelertSort {    /**     * 简单选择排序     * @param array     */    public static int[]  selectSort(int[] array) {        int min;        int tmp = 0;        for (int i = 0; i < array.length; i++) {            min = array[i];            for (int j = i; j < array.length; j++) {//j=i,从i开始对比                if (array[j] < min) {                    min = array[j];//最小值                    tmp = array[i];                    array[i] = min;                    array[j] = tmp;                }            }        }        return array;    }    @SuppressWarnings("unused")    public static void main(String [] args){        SelertSort selertSort = new SelertSort();        int[] array = selectSort(new int[]{9,4,2,6,7,3,10,33,88,1,17});        for(int num:array){            System.out.print(num + " ");        }    }}

3.5 堆排序

3.5.1 定义

堆的定义如下:具有n个元素的序列(h1,h2,…,hn),当且仅当满足(hi>=h2i,hi>=2i+1)或(hi<=h2i,hi<=2i+1) (i=1,2,…,n/2)时称之为堆。在这里只讨论满足前者条件的堆。由堆的定义可以看出,堆顶元素(即第一个元素)必为最大项(大顶堆)。完全二叉树可以很直观地表示堆的结构。堆顶为根,其它为左子树、右子树。初始时把要排序的数的序列看作是一棵顺序存储的二叉树,调整它们的存储序,使之成为一个堆,这时堆的根节点的数最大。然后将根节点与堆的最后一个节点交换。然后对前面(n-1)个数重新调整使之成为堆。依此类推,直到只有两个节点的堆,并对它们作交换,最后得到有n个节点的有序序列。

3.5.2 核心思想

核心思想:堆排序是一种树形选择排序,是对直接选择排序的有效改进。

堆排序的关键在于:1.建堆(大顶堆或小顶堆)2.拿堆的根节点和最后一个节点交换。

3.5.2 演示图

1.建堆:

这里写图片描述

2.交换,从堆中排除最大数:

这里写图片描述

3.剩余节点再建堆,再交换从堆中排除最大数

这里写图片描述

依次类推:最后堆中剩余的最后两个结点交换,踢出一个,排序完成。

存储模式:

这里写图片描述

3.5.3 源码

package com.dn.sort;//堆排序public class HeapSort {     /**     * 堆排序     *      * 核心思想:堆排序是一种树形选择排序,是对直接选择排序的有效改进。     * 堆排序的关键在于:1.建堆(大顶堆或小顶堆)2.拿堆的根节点和最后一个节点交换。     */    public void heapSort(int[] array) {        if (array == null || array.length <= 1) {            return;        }        // 建立最大堆        buildMaxHeap(array);        // 排序        for (int i = array.length - 1; i >= 1; i--) {            // 最大的在0位置,那么开始沉降,这样每交换一次最大的值就丢到最后了            exchangeElements(array, 0, i);            // 调整大堆            maxHeap(array, i, 0);//i--,不遍历最后的值        }    }    /**     * 建立最大堆     * @param array     */    private void buildMaxHeap(int[] array) {        if (array == null || array.length <= 1) {            return;        }        int half = (array.length - 1) / 2;//遍历一半(即可得到另一半,最深一层),假设有13个        for (int i = half; i >= 0; i--) {//从中间序号6开始            //调整大堆,只需遍历i=6543210            maxHeap(array, array.length, i);        }    }    /**     * 调整大堆️     * @param array 堆数组     * @param length 表示用于构造大堆的数组长度元素数量     * @param index 从哪位置开始     */    private void maxHeap(int[] array, int length, int index) {        int left = index * 2 + 1;//左节点        int right = index * 2 + 2;//右节点        int largest = index;//目标序号        //如果左右节点比根节点大,将左右序号需要赋值为目标序号        if (left < length && array[left] > array[index]) {//left < length安全范围内            largest = left;        }        if (right < length && array[right] > array[largest]) {            largest = right;        }        if (index != largest) {//largest位置元素不是最大值            //数据交换            exchangeElements(array, index, largest);            //继续调整大堆            maxHeap(array, length, largest);//largest==变化的值        }    }    /**     * 数据交换     */    public void exchangeElements(int[] array, int index1, int index2) {        int temp = array[index1];        array[index1] = array[index2];        array[index2] = temp;    }    /**     * 打印     */    public void printArray(int[] array) {        System.out.print("{");        for (int i = 0; i < array.length; i++) {            System.out.print(array[i]);            if (i < array.length - 1) {                System.out.print(", ");            }        }        System.out.println("}");    }    public static void main(String[] args) {        HeapSort heapSort = new HeapSort();        int[] array = { 19, 8, 27, 6, 35, 14, 3, 12, 1, 0, 9, 10, 7 };        System.out.println("Before heap:");        heapSort.printArray(array);        heapSort.heapSort(array);        System.out.println("After heap sort:");        heapSort.printArray(array);    }}

3.6 冒泡排序

3.6.1 核心思想

冒泡排序是最简单的排序算法之一,实现起来也比较简单

基本思想:其原理就是进行n-1趟比较并交换,小数往上冒,大数往下沉,经过n-1趟之后形成了有序的数列。

3.6.2 演示图

这里写图片描述

3.6.3 源码

public class BubbleSort {      public static void bubbleSort(int[] data){          for(int i=0; i<data.length-1; i++){              for(int j=0; j<data.length-i-1; j++){                  if(data[j] > data[j+1]){                      int temp = data[j];                      data[j] = data[j+1];                      data[j+1] = temp;                  }              }          }      }      public static void main(String[] args){          int data[] = {22,34,12,32,50,67,43,32};          System.out.print("排序前:");          for(int i=0; i<data.length; i++){              System.out.print(data[i]+" ");          }          System.out.println();          bubbleSort(data);          System.out.print("排序后:");          for(int i=0; i<data.length; i++){              System.out.print(data[i]+" ");          }      }  }  

3.7 快速排序

3.7.1 核心思想

快速排序死对 冒泡排序的一种改进。

基本思想是:选择一个基准元素,通常选择第一个元素或者最后一个元素,通过一趟扫描,将待排序列分成两部分,一部分比基准元素小,一部分大于等于基准元素,此时基准元素在其排好序后的正确位置,然后再用同样的方法递归地排序划分的两部分。

3.7.2 演示图

这里写图片描述

3.7.3 源码

package com.dn.sort;public class quickSort {    public static int getMiddle(int[] list, int low, int high) {        int tmp = list[low]; // 数组的第一个作为中轴        while (low < high) {            while (low < high && list[high] >= tmp) {                high--;            }            list[low] = list[high]; // 比中轴小的记录移到低端            while (low < high && list[low] <= tmp) {                low++;            }            list[high] = list[low]; // 比中轴大的记录移到高端        }        list[low] = tmp; // 中轴记录到尾        return low; // 返回中轴的位置    }    public static void quickSort(int[] list, int low, int high) {        if (low < high) {            int middle = getMiddle(list, low, high); // 将list数组进行一分为二            quickSort(list, low, middle - 1); // 对低字表进行递归排序            quickSort(list, middle + 1, high); // 对高字表进行递归排序        }    }    public static void main(String[] args) {        int a[] = { 49, 99, 56, 67, 18, 23, 34, 15, 35, 25, 73, 51 };        if (a.length > 0) { // 查看数组是否为空            quickSort(a, 0, a.length - 1);        }        for (int i = 0; i < a.length; i++)            System.out.print(a[i] + "  ");    }}

3.8 归并排序

3.8.1 核心思想

基本思想是:归并(Merge)排序法是将两个(或两个以上)有序表合并成一个新的有序表,即把待排序序列分为若干个子序列,每个子序列是有序的。然后再把有序子序列合并为整体有序序列。

细化来说,归并排序现将长度为n的无序序列看成是n个长度为1的有序子序列,首先做两两合并,得到n/2个长度为2的有序子序列,再做两两合并…不断重复这个过程,最终可以得到一个长度为n的有序序列。

3.8.2 演示图

这里写图片描述

这里写图片描述

3.8.3 源码

package com.dn.sort;public class MergeSort {    public void mergeSort(int[] a,int left,int right){        if(left<right){            int middle = (left+right)/2; //找出中间索引              mergeSort(a, left, middle); //对左边数组进行递归              mergeSort(a,middle+1,right); //对右边数组进行递归              merge(a,left,middle,right); //合并        }    }    private void merge(int[] a, int left, int middle, int right) {        //中间数组        int [] tmpArray = new int[a.length];        //右起第一个索引        int rightStart = middle+1;        //中间索引        int tmp = left;        //third记录中间数组的索引          int third = left;        //比较两个小数组相应下标位置的数组大小,小的先放进中间数组        while(left<=middle&&rightStart<=right){           //    4         10          3                          6             if(a[left]<=a[rightStart]){                                //  4>3 b   4<6 a   10>6 b     rightStart=4 > right=3   c                tmpArray[third++] = a [left++];//a             //    3           4           6                        10            }else{                                                                           tmpArray[third++] = a[rightStart++];//b            }        }        //如果左边还有数据需要拷贝,把左边数组剩下的拷贝到中间数组        while(left<=middle){            tmpArray[third++] = a[left++];//c        }        //如果右边还有数据......        while(rightStart<=right){            tmpArray[third++] = a[rightStart++];        }        //将中间数组中的内容复制回原数组          while(tmp<=right){            a[tmp] = tmpArray[tmp++];        }    }    public static void main(String[] args){        MergeSort mergeSort = new MergeSort();        int [] a = new int[]{10,4,6,3,8,2,5,7};              mergeSort.mergeSort(a, 0, a.length-1);        for(int n:a){            System.out.print(" "+n);        }    }}

3.9 基数排序

3.9.1 核心思想

基本思想是:将所有待比较数值(正整数)统一为同样的数位长度,数位较短的数前面补零。然后,从最低位开始,依次进行一次排序。这样从最低位排序一直到最高位排序完成以后,数列就变成一个有序序列。

3.9.2 演示图

这里写图片描述

个位:
这里写图片描述

十位:
这里写图片描述

百位:
这里写图片描述

3.9.3 源码

package com.dn.sort;import java.util.ArrayList;import java.util.List;public class BasicSort {    /**     * 基数排序     *      * @param array     */    @SuppressWarnings({ "rawtypes", "unchecked" })    public void basicSort(int[] array) {        int max = 0;// 获取最大值        for (int i = 0; i < array.length; i++) {            if (max < array[i]) {                max = array[i];            }        }        int times = 0;// 获取最大值位数        while (max > 0) {            max = max / 10;            times++;        }        //建立10个集合        List<ArrayList> baseList = new ArrayList<ArrayList>();// 多维数组        for (int i = 0; i < 10; i++) {            ArrayList list1 = new ArrayList<>();            baseList.add(list1);        }        //进行times次分配和收集;         for (int i = 0; i < times; i++) {            //分配数组元素            for (int j = 0; j < array.length; j++) {                // 获取对应的位的值(i为0是个位,i为1是10位,i为2是百位)                int x = array[j] % (int) Math.pow(10, i + 1) / (int) Math.pow(10, i);//取余、取整                ArrayList list2 = baseList.get(x);                list2.add(array[j]);// 把元素添加进对应下标数组                // queue.set(x,q);//待定            }            int count = 0;//元素计数器;                 //收集队列元素;            for (int j = 0; j < 10; j++) {//0-9                while (baseList.get(j).size() > 0) {                    ArrayList<Integer> list3 = baseList.get(j);// 拿到每一个集合                    array[count] = list3.get(0);//获取集合第一个,每次都是新的数据                    list3.remove(0);//删除集合第一个                    count++;                }            }        }    }    public static void main(String[] args) {        BasicSort basicSort = new BasicSort();        int[] a = { 136, 2, 6, 8, 9, 2, 8, 11, 23, 56, 34, 90, 89, 29, 145,209, 320, 78, 3 };        basicSort.basicSort(a);        for (int n : a) {            System.out.print(" " + n);        }    }}
0 0
原创粉丝点击