몇 가지 일반적인 분류 방법, 성능 분석, 그리고 C 언어에 대한 기본 소개

이 문서에서는 일곱 일반적인 정렬 알고리즘뿐만 아니라, 자신의 원칙 및 성능 분석 C 언어를 설명합니다 :

일관성, 사용하려면 모든 알고리즘을 모두 오름차순으로 정렬 본원 해석

 

우선 [] 무질서한 배열 요소의 도착을 준비 배열 길이의 길이, 스왑 함수 스왑

구현은 기본 기능에 정렬 함수를 호출하고 출력 결과를 정렬 :

보이드 스왑 (INT * X, INT * Y) {
    INT 온도 = * X;
    * * X = Y;
    * Y = 온도; } 값 int () {메인 INT의 도착 [] = {1,8,5,7,4,6,2,3 }; INT = 길이를 sizeof (도착) /를 sizeof (INT ); 종류 (편곡, 길이); 대해 INT (I = 0; I <길이; I ++ ) {printf와 ( "% D \ n" , 도착 [I]); } 0을 반환 ; }

 


 

삽입 정렬

첫 번째주기 :

두 번째주기 :

세 번째주기 :

 

 외부 루프의 각 실행 무질서 영역에서 도착 순서로 데이터 영역에 삽입된다 [I]

  데이터는 이전보다 더 작은 경우, 루프 제어에 삽입 된 레이어 데이터가, 언 [I]는 그 직전의 데이터 비교의 전과 변화 후 데이터하자

  다음과 같은 데이터를 이동 한 후에 도착 등 ... 도착보다 찾을 때까지 상기 단계를 반복하는 [I] 도착 소형 데이터 [J], [J]를 직접적 [I]에서, 언 자유 도착 [J + 1] 위치

 

C 프로그램 구현 :

보이드 CRsort (INT의 도착 [], INT 길이) {
    INT 온도;
    경우 (나는 <길이; INT 나 0 = I ++ ) {
        임시은 = [I], 언; 대 (INT J = I - 1; J> = 0; j-- ) { (도착 [J]> 만약 온도) {도착 [J + 1] = 도착 [J] 사용한다} else {도착 [J + 1] = 온도; 휴식 ; }}}}

 

성능 분석

안정성 : 안정             

  -> 내부 루프 실행 후, 도착 만 동일 할 것이다 [I] 시프트 도착 이상 발생 [I]는 변화되지 않는다

시간 복잡도 (최악 n², 바람직하게는 N, 평균 N²)    

  -> 데이터의 양이 N이고, n은 데이터가 정렬되면 회 외부 루프는 n 배의 최대 내부 루프 만 내부 루프를 실행할

   더 순차적 데이터, 높은 효율의 삽입 정렬

우주의 복잡성 : 1

  -> 재귀를 사용하지 않는 프로그램, 임시 변수는 스토리지 자원, 하나의 복잡한 공간을 차지하지 않는다

 


 

 쉘 정렬

쉘 정렬, "증가 종류의 확대"라는 향상된 삽입 정렬 현재 행입니다 :

삽입 정렬 장점 단점 힐 정렬이 두 특성을 이용하여, 하나의 소자 각각 비교 한 근접 정렬 요소를 처리하기에 적합하다 :

 

통상 삽입 정렬 달리 셸 정렬 가지고 점진적 시퀀스 R 일반적으로, R의 초기치 및 INT (길이 / 2) 2 라운드 다운 나눈 요소 즉 수가

 모든 단위는 떨어져 요소 그룹은 그룹의 순서를 수행 r에

모든 바이너리 증가는 마지막 종류까지, 하나의 단위로, 다음 요소는 주문해야합니다

 

델타 최 외측 루프 제어 그룹, 즉, 각 사이클 증분

  루프 내부에는 두가지 기본 삽입 일종이지만, 각각의 부가를 순서화 된 세트 도착하지 [I + 1] 그러나 도착 [I + 델타]

 C 프로그램 구현 :

보이드 셸 정렬 ( INT의 도착 [], INT 길이) {
     INT를 R, 온도, J;
     (길이 = R / 2 , R> = 1 , R = R / 2 ) {
          ( INT I = R; I <길이; I ++ ) {
            임시 =은 [I], 언;
            J는 I를 = - (R)를;
            반면 (j> = 0 && 온도 < 도착 [J]) {
                , 언 [J + R = 도착 [J]
                J는 J가 = - (R)를;
            }
            도착 [J + R = 온도;
        }
    }
}

성능 분석

안정성 : 불안정한             

  -> 각 요소 자체 정렬 기이고, 두 그룹의 다른 요소에 대해 동일한 값이 다를 수 전체적인 상대 위치를 정렬

시간 복잡도 : (최악의 n², 바람직하게는 N, 평균 N ^ 1.3)    

  -> 정렬 분석 셸은 시간 인터넷에서 촬영 미해결 수학 문제의 수를 포함한다 "증분"연속 촬영 기능을인지, 복잡한 문제이다.

    데이터가 정렬되면 바람직하게는 동일한 일반적인 삽입 정렬 만 내부 루프를 실행할

   주문 데이터의 실행의 높은 효율 : 또한 정렬 삽입의 특성을 갖는다

우주의 복잡성 : 1

  -> 재귀를 사용하지 않는 프로그램, 임시 변수는 스토리지 자원, 하나의 복잡한 공간을 차지하지 않는다

 


 

버블 정렬

버블 정렬

 

 

외부 루프 요소마다 정렬 영역을 배치

  내측 루프 제어 요소로 : 설정 포인터 j는, [J]의 값을 가지고 도착 [J + 1], 언 비교 :

  만일 도착 [J] <도착 [J + 1] 장애 비교 완료까지 ++ [J]와, 언 값 [J + 1] 과거 또는 J를, 언 것

이 버블 정렬을 최적화 할 수 있음을 언급 할만큼 가치입니다 :

변경 세트가있을 때 상태 코드 변경 설정, 데이터 교환이 일어난다

당신이 정렬 교환이 일어나는 풀 타임이없는 경우,이는 직접 사이클을 종료 할 수 있습니다, 데이터의 정렬 된 세트입니다

C 프로그램은 달성하기 위해 :

INT MPsort (INT 도착 [], INT의 길이) {
    INT 온도 변화 = 0 ;
    경우 (나는 <길이; INT 나 0 = I ++ ) {
        for (int j = 0;j < length - i - 1;j++) {
            if (arr[j] > arr[j + 1]) {
                swap(&arr[j],&arr[j+1]); change = 1; } } if (change == 0) { return 0; } } }

性能分析

稳定性 : 稳定             

  -->数据只有在大于或小于时才会交换,相等时不会交换,因此相同数据的相对位置不会发生改变

时间复杂度 : (最坏n²,最好n,平均n²)    

  -->在数据完全有序时,不会有数据交换,根据上面的优化处理,状态码change不会改变,外层循环执行一次就结束,时间复杂度为n+1,也就是n.

    而如果是非常无序的数据,外层循环执行满n次,时间复杂度就是n²

空间复杂度: log(2)(N)

  -->程序没有用到递归,临时变量不占用存储资源,因此空间复杂度为1

 


 

快速排序

快速排序是一种效率比较高的排序方法,这张图片我认为介绍的很清楚,搬运来用一下,出处在文章下面:

c程序实现:

void KSSort(int arr[], int left, int right) {
    if (left < right) {
        int key = arr[left];
        int l = left;
        int r = right;
        while (l < r) { while (l < r && arr[r] >= key) { r--; } if (l < r) { arr[l] = arr[r]; l++; } else { break; } while (l < r && arr[l] <= key) { l++; } if (l < r) { arr[r] = arr[l]; r--; } else { break; } } arr[l] = key; KSSort(arr, left, l - 1); KSSort(arr, l + 1, right); } }

性能分析

稳定性 : 不稳定 

  --> 假设是稳定的,举个反例: 

    5 | 3 1 2 | 9 7 8 9 | 4 6 3

    这时遍历unvisited部分 刚到了4 (array[8])

    显然4<5 ,这是4应该从 unvisited 部分去到 lower 部分。 因此 higher部分第一个元素 9 (array[4]) 和 4互换。变成了这样:

    5 | 3 1 2 4 | 7 8 9 9 | 6 3

时间复杂度 : (最坏n²,最好nlogn,平均nlogn)    

  -->快速排序最差的情况就是每次取到的基准数baes都是排序组的边界值(不是最小的就是最大的),这时外层循环要遍历n次才能将所有数据比较完,时间复杂度就是n²

    这种情况多发生在排好序(或接近排好序)的数据中,要在这种数据中避免使用快速排序算法

   最好的情况是基准数每次都能取到接近排序组的中位数,用最短的循环次数将程序完全分割,n个数据每次减半,也就是log(2)(N)次后数据被完全分割,算法的时间复杂度为

   最差的情况不容易取到,平均时间复杂度取nlogn

空间复杂度: logn

  -->在我这个程序里没有用到递归,空间复杂度为1,当然也可以使用递归实现,递归log(2)(N)次,空间复杂度为logn

最后附一张各种排序算法比较图

 


 

选择排序

选择排序是最简单的排序方式,也是比较低效的一种排序方式:

第一次循环 :

 

 第二次循环:

 

 第三次循环:

 

外层循环每执行一次向有序区添加一个元素

  内层循环遍历到最大的元素,与刚刚填入有序区的元素交换

......直到数据全部加入有序区

c程序实现:

void XZsort(int arr[] , int length) {
    int check;
    for (int i = 0;i < length - 1;i++) {
        check = i;
        for (int j = i + 1;j < length;j++) {
            if (arr[j] < arr[check]) {
                check = j;
            }
        }
        if (i != check) {
            swap(&arr[i],&arr[check]);
        }
    }
}

 

性能分析

稳定性 : 稳定

  -->存在两个相同的元素时,肯定是下标小的元素先进入有序区,而且在值相等的情况下有序区元素不会被替换

时间复杂度 : (最坏n²,最好n²,平均n²)

  -->外层循环要执行n次,才能将n个数据全部加入有序区

   无论数据是否有序,内层循环都要将所有的数据比较一遍,找到最小的元素

空间复杂度: 1

  -->程序没有用到递归,临时变量不占用存储资源,因此空间复杂度为1

 


 

堆排序

堆排序是选择排序的改进版本,它比堆排序的性能要高很多.

要实现堆排序,首先要了解这几个知识点:

大根堆:每个节点的值都大于他的左右子树(小根堆反之)

完全二叉树:除了最后一层之外的其他每一层都被完全填充,并且所有结点都保持向左对齐。 

 在n个节点的完全二叉树中,叶子节点有(n+1)/2个,非叶子节点有(n-1)/2个

 在堆中,下标为n的数的左子树为2n+1,右子树为2n+2

 

首先要能够将一组无序的数排列成大根堆,大根堆的构造方法如下:

 

               

 

 

            

 

 ...

外层循环从最后一个非叶子节点( length/2-1 )向根节点遍历,每遍历到一个数据,就执行下面操作:

设置一个根指针i,指向当前要操作树的根节点

比较该树的左右子树的值,将biger指针指向较大的子树

如果该子树的值比根节点还大(arr[biger]>arr[i]),将该子树的值与根节点交换,并将i指针指向该子树,使之成为新的根节点

  ......递归执行上一步操作,直到有根节点不小于左右子树为止

 

 构造出大根堆之后,每次取整个堆的根节点(也就是第一个元素)存入有序区,将堆的最后一个元素作为根节点

剩下的元素继续构造大根堆,直到数据完全存入有序区

c程序实现:

void MkHeap(int arr[], int i, int length) {
    int bigger = 2 * i + 1;
    int temp;
    if (bigger<length){
        if (arr[bigger] < arr[bigger + 1]) {
            bigger++;
        }
        if (arr[i]<arr[bigger]){//如果子树比爹树大:把子树的值与爹树值交换,并让该子树成为新的爹树
            swap(&arr[i], &arr[bigger]);
            MkHeap(arr,bigger,length);    
        }
    }
}
void HeapSort(int arr[], int length) {
    //从最后一个非叶子节点往根找
    for (int i = length / 2 - 1;i >= 0;i--) {
        MkHeap(arr, i, length);
    }
    for (int j = length - 1;j > 0;j--) {
        swap(&arr[j],&arr[0]);
        MkHeap(arr, 0, j-1);
    }
}

性能分析

稳定性 : 不稳定             

  -->先假设稳定,然后举个反例:

   {6,7,7}这棵树左右子树的值都是7,本来左子树是应该是在前面的,但是6加入有序区之后右子树的7会被提到最上面,这样他们的顺序就被调换了

时间复杂度 : (最坏nlogn,最好nlogn,平均nlogn   <底数可以写2也可以不写,根据时间复杂度的性质无论底数是几结果都是一样的>   )    

  -->根据性质,外层循环会执行n次(n代表排序的元素个数),将所有数据全部加入有序区.

   而内层递归函数指针从0到n,每次增长前一个的2n+1,忽略掉常数1,也就是执行log(2)(N)次

   因此为Nlog(2)(N)次,忽略掉底数2,时间复杂度为nlogn

   插入/希尔排序适合接近有序的数据,而堆排序适合非常无序的数据,因为无论数据多么杂乱,希尔排序的时间复杂度都是nlogn

     (不放心再说一下 : log(2)(N)是log以2为底n的对数 , 不是log2乘n , 因为底数打不出来只能这样写)

空间复杂度: log(2)(N)

  -->这里程序递归了log(2)(N)次,每次递归都占用内存,因此空间复杂度为log(2)(N)

   当然也可以使用循环的方式实现,但是那样写出来结构略显混乱,不如递归方式清晰

 

 

 不稳定算法口诀:快些选队

 

参考资料(文中的部分图片和思想来自以上材料):

1. 《新编数据结构习题与解析》

2. 文章

https://www.cnblogs.com/skywang12345/p/3603935.html

https://www.cnblogs.com/jingmoxukong/p/4302891.html

http://www.sohu.com/a/341037266_115128

https://www.toutiao.com/a6593273307280179715/?iid=6593273307280179715

3. 关于快速排序算法的稳定性? - 知遥其实是德鲁伊的回答:https://www.zhihu.com/question/45929062/answer/262452296

추천

출처www.cnblogs.com/iszhangk/p/11942617.html