哈尔滨学院夏令营day2-1:排序上

排序

概念化的东西就不说了,直接上我认为有用的。
排序算法往往是题的切入点,很多算法的正确性是依赖于数据的有序性的。我们要理解排序算法的每一步并能用手一字不差写出代码一开始我也觉得一个sort函数就搞定了,但远远不够。我感觉花里胡哨的能排出花儿来
不同的排序算法有对应的数据规模,合适的算法可以节省大量的资源。
分为两大类:比较类和非比较类(排序时是否需要数字与数字之间比较)
比较类:交换排序、冒泡排序、快排、插入排序、简单插入排序、希尔排序、选择排序、简单选择排序、堆排序、归并排序、二路归并排序、多路归并排序
非比较类:计数排序、桶排序、基数排序

1.冒泡排序:

数字小的话泡就小,反之就大,然后就冒泡,大的沉下去,小的冒上去。一次只能解决一个数的位置效率不高。
描述:
比较相邻元素,前面比后面的大就交换他们俩。从开始一直到最后两两做此操作,最后的元素就是最大的了。然后一致这样重复,直至排序完成。
代码:

int bubblesort(int arr[],int len)
{
    for(int i=0;i<len-1;i++)
    {
        for(inr j=0;j<len-1;j++)
        {
            if(arr[j]>arr[j+1])
                swap(arr[j],arr[j+1]);
        }
    }
}

时间复杂度:O(n^2)
空间复杂度:O(1)(在原数组中排)

2.选择排序:

描述:
在数组中找到最小(大)的值,放到0位置,再找最小(大)的值放到1位置,以此类推。
代码:

int selectionsort(int arr[],int len)
{
    for(int i=0;i<len-1;i++)
    {
        int minindex=i;
        for(int j=i+1;j<len;j++)
        {
            if(arr[j]<arr[minindex])
            {
                minindex=j;
            }
        }
        swap(arr[i],arr[minindex]);
    }
}

时空复杂度同冒泡排序

3.插入排序:

就像玩扑克牌,抓牌然后排序往里插着放成一个扇形(这一直是我小时候最不会干的,拿着拿着就散架了)
描述:
从第一个元素开始,把它当成目标有序数列,在未排序的数列中挨个拿元素,往目标数列里插。

代码:

int insertsort(int arr[],int len)
{
    for(int i=1;i<len;i++)
    {
        int index=i-1;
        int current=arr[i];
        while(index>=0&&arr[index]>current)
        {
            arr[index+1]=arr[index];
            index-=1;
        }
        arr[index+1]=current;
    }
}

时空复杂度同冒泡排序

4.希尔排序:

分组的插入排序。
举栗子:
55 2 6 4 32 12 9 73 26 37
第一轮:
10/2=5
以5为分量进行分组(看下标)
55与12,2与9,6与73,4与26,32与37
每个分组按照插入排序进行排序。
12与55,2与9,6与13,4与26,32与37
第二轮:
5/2=2
以2为长度进行分组
55 6 32 9 26为一组
2 4 12 73 37为一组
对组内元素进行插入排序
6 9 26 32 55
2 4 12 37 73
第三轮:
2/2=1
对整个数组进行插入排序。
代码:

int shellsort(int arr[],int len)
{
    for(int group=len/2;group>0;group/=2)
    {
        for(int i=group;i<len;i++)
        {
            int index=i;
            int current=arr[i];
            while(index-group>=0&&arr[index-group]>cyrrent)
            {
                arr[index]=arr[index-group];
                index=index-group;
            }
            arr[index]=current;
        }
    }
}

希尔排序是第一个突破O(n^2)的排序算法,在使用插入排序时插入规模变小,效率有所提升。
时间复杂度O(n^(3/2))
空间复杂度O(1)

5.归并排序:

递归,分治思想(把活儿一个个往下分,干完后往上提交)。
代码:

#include<stdio.h>
#include <iostream>
#include<stdlib.h>
using namespace std;

void merge(int arr[], int L, int mid, int R) {
    int i = L, j = mid + 1, k = 0;
    int *help = (int *) malloc(sizeof(int) * (R - L + 1));

    while (i <= mid && j <= R) {
        help[k++] = arr[i] < arr[j] ? arr[i++] : arr[j++];
    }
    while (i <= mid) {
        help[k++] = arr[i++];
    }
    while (j <= R) {
        help[k++] = arr[j++];
    }
    for (int i = 0; i < k; i++) {
        arr[L + i] = help[i];
    }
}

void MergeSort(int arr[], int L, int R) {
    if (L == R) {
        return;
    }
    int mid = L + ((R - L) >> 1);
    MergeSort(arr, L, mid);
    MergeSort(arr, mid + 1, R);
    merge(arr, L, mid, R);
}

int main() {
    srand(time(NULL));
    int arr[100001], n;
    //cin >> n;
    n = rand() % 100 + 1;
    for (int i = 0; i < n; i++) {
        //cin >> arr[i];
        arr[i] = rand() % 1000 + 1;
    }
    MergeSort(arr, 0, n - 1);
    for (int i = 0; i < n; i++)
        printf("%d ", arr[i]);
    return 0;
}

时间复杂度O(n*logn)
空间复杂度O(n)

例题
小和问题
在一个数组中,每一个数左边比当前数小的数累加起来,叫做这个数组的小和,求一个数组的小和。
例子:1 3 4 2 5
1左边比1小的数,没有。
3左边比3小的数,1.
4左边比4小的数,1,3.
2左边比2小的数,1.
5左边比5小的数,1,3,4,2.
所以小和为1+1+3+1+1+3+4+2=16.
方法一:
暴力遍历所有比当前小的数进行累和

int function(int arr[],int len)
{
    int sum=0;
    for(int i=1;i<len;i++)
    {
        for(int j=0;j<i;j++)
        {
            if(arr[j]<arr[i])
                sum+=arr[j];
        }
    }
}

方法二:
在归并过程中计算小和

6.快速排序:

引入:荷兰国旗问题
给定一个数组arr,和一个数num,请把小于等于num的数放在数组左边,大于num的数放在数组右边。
要求额外空间复杂度O(1),时间复杂度O(n)
最终保证0到l为小于等于num区域,l+1到end为大于等于num区域。
思路:在遍历的过程中找到了小于等于num的区域,同时也找到了很多大于num的数,始终让小于等于num的区域推着大于num的数往前走。
l初始化为-1。
i用来遍历,从零开始。
arr[i]<=num swap(arr[i],arr[l+1]) l++; i++;
arr[i]>num i++
经典快排:以待排序列的最后一个元素x作为标准,将整个序列划分为两个部分,小于等于x以及大于等于x的两个区域,然后将这两个部分递归这个过程。

#include<stdio.h>
#include <iostream>
#include<stdlib.h>
using namespace std;

int *partition(int arr[], int L, int R) {
    int left = L - 1, right = R + 1, current = L;
    int *p = (int *) malloc(2 * sizeof(int));
    int num = arr[R];
    while (current < right) {
        if (arr[current] < num) {
            swap(arr[current], arr[left + 1]);
            left++;
            current++;
            //swap(arr[current++], arr[++left]);
        } else if (arr[current] == num) {
            current++;
        } else {
            swap(arr[current], arr[right - 1]);
            right--;
            //swap(arr[current++], arr[--right]);
        }
    }
    p[0] = left + 1;
    p[1] = right - 1;
    return p;
}

void QuickSort(int arr[], int L, int R) {
    if (L < R) {
        int *p = partition(arr, L, R);
        QuickSort(arr, L, p[0] - 1);
        QuickSort(arr, p[1] + 1, R);
    }
}

int main() {
    srand(time(NULL));
    int arr[100001], n;
    //cin >> n;
    n = rand() % 100 + 1;
    for (int i = 0; i < n; i++) {
        //cin >> arr[i];
        arr[i] = rand() % 1000 + 1;
    }
    QuickSort(arr, 0, n - 1);
    for (int i = 0; i < n; i++)
        printf("%d ", arr[i]);
    return 0;
}

缺点:如果每次的标准数正好是最大或者最小值,此时每次partirion过程结束后只能确定一个数的位置,这样时间复杂度就O(n^2)了。
随机快排:
标准数进行随机化,整个序列划分三个部分,小于,等于,大于。小于和大于部分递归partition过程。会产生额外空间复杂度logn,原因是每一次partition过程会额外记录三个划分点。

#include<stdio.h>
#include <iostream>
#include<stdlib.h>
using namespace std;

int *partition(int arr[], int L, int R) {
    int left = L - 1, right = R + 1, current = L;
    int index = L + rand() % (R - L + 1);
    int *p = (int *) malloc(2 * sizeof(int));
    int num = arr[index];
    while (current < right) {
        if (arr[current] < num) {
            swap(arr[current], arr[left + 1]);
            left++;
            current++;
            //swap(arr[current++], arr[++left]);
        } else if (arr[current] == num) {
            current++;
        } else {
            swap(arr[current], arr[right - 1]);
            right--;
            //swap(arr[current++], arr[--right]);
        }
    }
    p[0] = left + 1;
    p[1] = right - 1;
    return p;
}

void QuickSort(int arr[], int L, int R) {
    if (L < R) {
        int *p = partition(arr, L, R);
        QuickSort(arr, L, p[0] - 1);
        QuickSort(arr, p[1] + 1, R);
    }
}

int main() {
    srand(time(NULL));
    int arr[100001], n;
    //cin >> n;
    n = rand() % 100 + 1;
    for (int i = 0; i < n; i++) {
        //cin >> arr[i];
        arr[i] = rand() % 1000 + 1;
    }
    QuickSort(arr, 0, n - 1);
    for (int i = 0; i < n; i++)
        printf("%d ", arr[i]);
    return 0;
}

综上,
快排时间复杂度O(n*logn)
空间复杂度O(logn)

==================================================

唉,day2的知识量太大了,终于搞完了一部分,就在刚刚,又收到了额外布置的练习题,虽说可以不做,可我心里过意不去啊。后几个重点的排序掌握程度还不能达到要求的标准,今天又经历了不少事儿,有些烦躁,但还是坚持继续写了这个。迫不得已总得往下走啊,哦,不对,是跑啊。

©️2020 CSDN 皮肤主题: 1024 设计师:上身试试 返回首页