排序

排序方法:插入、选择、交换、归并、分配排序

插入排序

直接插入排序

流程图:

img

代码实现:

void InsertSort(int arr[]){
     for (int i = 1; i < arr.length; i++) {
            //需要插入的数据
            int temp = arr[i];
            int j = i - 1;
            //当插入的数据小于前面的数据时
            while (j >= 0 && arr[j] > temp) {
                //将插入的数据的前面的数据向后移动
                arr[j + 1] = arr[j];
                j--;
            }
            //插入数据
            arr[++j] = temp;
        }
}

特点:数据有序程度越高,越有效

适应场景:小规模数据或者基本有序时十分高效。

希尔排序

流程图:

img

代码实现:

void shellSort(int arr[]){
    int len = arr.length;

     //增量gap, 并逐步的缩小增量
    for (int gap = len / 2; gap >= 1; gap /= 2) {
        // 从第gap个元素,逐个对其所在的组进行直接插入排序
        for (int i = gap; i < len; i++) {
            int j = i;
            int temp = arr[j];
            if (arr[j] < arr[j - gap]) {
                //移动
                arr[j] = arr[j - gap];
                j -= gap;
            }
            //当退出while后,就给temp找到插入的位置
            arr[j] = temp;
        }
    }
}

交换排序

冒泡排序

代码实现:

void bubbleSort(int a[], int n) {   //下面是函数bubbleSort的程序 
    //定义三个整型变量 
    int i,j,temp;   
    //用一个嵌套循环来遍历一遍每一对相邻元素 (所以冒泡函数慢嘛,时间复杂度高) 
    for (j=0;j<n-1;j++){     
        for (i=0;i<n-1-j;i++) {
            if(a[i]>a[i+1]) { //从大到小排就把左边的">"改为"<" !!!
                temp=a[i];      //a[i]与a[i+1](即a[i]后面那个) 交换
                a[i]=a[i+1];    //基本的交换原理"c=a;a=b;b=c" 
                a[i+1]=temp;
            }
        }
    }    
}

快速排序(双向指针)

流程图:8

img

代码实现:

void quicksort(int arr[], int left, int right) {
	if (left > right) return;
        //基准
        int temp = arr[left];
        int i = left;
        int j = right;
        //顺序很重要,要先从右边开始查找
        while (i < j) {
            //从右向左移动找到第一个小于基准值
            while (arr[j] >= temp && i < j) {
                j--;
            }
            //从左向右移动找到第一个大于基准值
            while (arr[i] <= temp && i < j) {
                i++;
            }
            //满足条件则交换
            if (i < j) {
                int t = arr[j];
                arr[j] = arr[i];
                arr[i] = t;
            }
        }
        //最后将基准i和j相等位置的数字交换
        arr[left] = arr[i];
        arr[i] = temp;
        quickSort(arr, left, i - 1);
        quickSort(arr, i + 1, right);
}

选择排序

直接选择排序

流程图:

img

代码实现:

// 选择排序:相邻两个元素进行比较,把大的元素的下标记录下来,一轮完整的比较之后,若最大值的下标不是len-i,则两个元素交换位置
void selectSort(int arr[],int len) {
    int len = arr.length;
    //i表示次数,共进行n-1次选择和交换
    for (int i = 0; i < len; i++) {
        //用min表示最小元素的下标
        int min = i;
        //找到当前排序期间的最小元素的下标
        for (int j = i + 1; j < len; j++) {
            //如果后面的元素小于当前最小元素,用min记录下来后面最小元素的下标
            if (arr[j] < arr[min]) {
                min = j; //保存位置
            }
        }
        //如果min==i,说明min就是排序区间最小的不用交换
        if (min != i) {
            int temp = arr[i];
            arr[i] = arr[min];
            arr[min] = temp;
        }
    }
}

时间复杂度:O(\(n^2\)) 空间复杂度:O(1)

堆排序(大顶堆 、小顶堆 )

利用完全二叉树的结构来维护的一维数组

创建最大堆 :

img

堆排序(每次取最大值与叶子节点互换移除掉最大元素) :

img

代码实现:

   public static void heapSort(int arr[]) {
        int len = arr.length;

        //创建堆
        for (int i = (len - 1) / 2; i >= 0; i--) {
            //从第一个非叶子结点从下至上,从右至左调整结构
            adjustHeap(arr, i, len);
        }

        //调整堆结构+交换堆顶元素和末尾元素
        for (int i = len - 1; i > 0; i--) {
            //将堆顶元素与末尾元素进行交换
            int temp = arr[i];
            arr[i] = arr[0];
            arr[0] = temp;

            //重新对堆进行调整
            adjustHeap(arr, 0, i);
        }
    }

    /**
     * 调整堆
     *
     * @param arr
     * @param parent
     * @param length
     */
    private static void adjustHeap(int arr[], int parent, int length) {
        //将temp作为父节点
        int temp = arr[parent];

        //左孩子
        int lChild = 2 * parent + 1;
        while (lChild < length) {
            //右孩子
            int rChild = lChild + 1;
            //如果有右孩子结点,并且右孩子结点的值大于左孩子结点,则选取右孩子结点
            if (rChild < length && arr[lChild] < arr[rChild]) {
                lChild++;
            }
            // 如果父结点的值已经大于孩子结点的值,则直接结束
            if (temp > arr[lChild]) {
                break;
            }
            //把孩子及诶单的值赋值给父节点
            arr[parent] = arr[lChild];
            //选取孩子结点的左孩子结点,继续向下筛选
            parent = lChild;
            lChild = 2 * lChild + 1;
        }
        arr[parent] = temp;
    }

归并排序

先分后归(先拆分为一个个有序的子序列,再将一个个有序子序列进行归并操作最后合并为一个有序的序列)

流程图:

img

复杂度

算法 最好情况 平均情况 最坏情况 空间复杂度 稳定性
归并排序 O(n log n) O(n log n) O(n log n) O(n) 稳定

分配排序

箱排序(桶排序)

流程图:

这里写图片描述

基数排序(分配和收集 )

(1)假设有欲排数据序列如下所示:

73 22 93 43 55 14 28 65 39 81

首先根据个位数的数值,在遍历数据时将它们各自分配到编号0至9的桶(个位数值与桶号一一对应)中。

分配结果(逻辑想象)如下图所示:

img

分配结束后。接下来将所有桶中所盛数据按照桶号由小到大(桶中由顶至底)依次重新收集串起来,得到如下仍然无序的数据序列:

81 22 73 93 43 14 55 65 28 39

接着,再进行一次分配,这次根据十位数值来分配(原理同上),分配结果(逻辑想象)如下图所示:

img

分配结束后。接下来再将所有桶中所盛的数据(原理同上)依次重新收集串接起来,得到如下的数据序列:

14 22 28 39 43 55 65 73 81 93

内部排序的方法比较

时间复杂度

(1)直接插入、直接选择、冒泡排序算法的时间复杂度为O(\(n^2\))

(2)快速、归并、堆排序算法的时间复杂度为O(\(nlog_{2}n\))

(3)希尔排序算法的时间复杂度很难计算:O(n\(log_{2}n\))或O(\(n^{1.25}\))

(4)基数排序算法的时间复杂度为O(d * (rd+n)),其中rd是基数,d是关键字的位数,n是元素个数。

稳定性

(1)直接插入、冒泡、归并和基数排序算法的稳定的。

(2)直接选择、希尔、快速和堆排序是不稳定的。

辅助空间

(1)直接插入、直接选择、冒泡、希尔和堆排序算法需要辅助空间为O(1).

(2)快速排序算法需要辅助空间为O(\(log_{2}n\))

(3)归并排序算法需要辅助空间为O(n)

(4)基数排序算法需要辅助空间为O(n+rd)

posted @ 2019-10-25 18:48  snail灬  阅读(262)  评论(0编辑  收藏  举报