zoukankan      html  css  js  c++  java
  • 数据结构学习笔记排序 (冒泡、插入、希尔、堆排序、归并排序)

    数据结构学习笔记排序 (冒泡、插入、希尔、堆排序、归并排序)

    • 前提void X_Sort ( ElementType A[], int N )

    • 大多数情况下,为简单起见,讨论从小大的整数排序

    • N是正整数

    • 只讨论基于比较的排序(> = < 有定义)

    • 只讨论内部排序

    • 稳定性:任意两个相等的数据,排序前后的相对位置不发生改变

    • 没有一种排序算法是任何情况下都表现最好的

    冒泡排序

    • (从小到大排序)物理意义:大泡泡往下沉,小泡泡往上冒

    • 每次比较相邻两个泡泡,符合条件,交换位置,每一轮比较完,最大的泡泡沉到最底下。

    • 最好情况:顺序T = O( N )

    • 最坏情况:逆序T = O( N^2 )

    注意

    • 设置交换标识
    • 坏处:逆序复杂度比较大,好处:当时单向链表排序的时候,适合(从上到下,相邻交换);严格大于的时候才交换,即有:稳定

    实现

    void Bubble_Sort(ElementType A[], int N)
    {
    	for (int P = N - 1; P >= 0;P--)
    	{
    		int flag = 0;
    		for (int i = 0; i < P; i++)  /*一趟冒泡排序*/
    		{
    			if (A[i]>A[i+1])
    			{
    				int temp = A[i];
    				A[i] = A[i + 1];
    				A[i + 1] = temp;
    				flag = 1;/*标识发生了交换*/
    			}
    		}
    		if (flag==0)
    		{
    			break; /*全程无交换*/
    		}
    	}
    }
    
    

    插入排序

    • (从小到大排序) 和打扑克摸牌差不多,每次摸牌从最后往前依次进行比较,需插入的牌小,往前比较,找到合适位置插入。

    • 最好情况:顺序T = O( N )

    • 最坏情况:逆序T = O( N^2 )

    注意

    • 稳定

    实现

    void Insertion_Sort(ElementType A[], int N)
    {
    	for (int P = 1; P < N;P++) //第一张已在手
    	{
    		int temp = A[P];/*摸一下张*/
    		for (int i = P; i>0 && A[i - 1] > temp;i--)
    		{
    			A[i] = A[i - 1]; //依次与已排序序列中元素比较并右移
    		}
    		A[P] = temp;
    	}
    }
    
    

    时间复杂度下界(womiga)

    • 逆序对(inversion)I逆序对的个数
    • 交换2个相邻元素正好消去1个逆序对
    • 插入排序:T(N+I)=O(N+I)
    • 如果序列基本有序,则插入排序简单且高效

    希尔排序

    • 原始希尔排序DM = [N / 2]向下取整 , Dk = [D(k+1) / 2]向下取整

    • 最坏情况: T =Ο( N^2 ) 不稳定

    • 增量元素不互质,则小增量可能根本不起作用

    • 更多的增量序列

    实现

    //原始的shell排序--theta(N*N)
    void Shell_Sort(ElementType A[], int N)
    {
    	for (int D = N / 2; D > 0; D++) /*希尔增量序列*/
    	{
    		for (int P = D; P < N;P++) //插入排序
    		{
    			int temp = A[P];
    			for (int i = P; i >= D&&A[i - D]>temp;i-=D)
    			{
    				A[i] = A[i - D];
    			}
    			A[P] = temp;
    		}
    	}
    }
    
    void ShellSort(ElementType A[], int N)
    { /* 希尔排序 - 用Sedgewick增量序列 */
    	int Si, D, P, i;
    	ElementType Tmp;
    	/* 这里只列出一小部分增量 */
    	int Sedgewick[] = { 929, 505, 209, 109, 41, 19, 5, 1, 0 };
    
    	for (Si = 0; Sedgewick[Si] >= N; Si++)
    		; /* 初始的增量Sedgewick[Si]不能超过待排序列长度 */
    
    	for (D = Sedgewick[Si]; D > 0; D = Sedgewick[++Si])
    	for (P = D; P<N; P++) { /* 插入排序*/
    		Tmp = A[P];
    		for (i = P; i >= D && A[i - D]>Tmp; i -= D)
    			A[i] = A[i - D];
    		A[i] = Tmp;
    	}
    }
    
    

    堆排序

    • 回顾选择排序

    • 选择排序交换的最多N次;关键在每次找到最小元素位置,简单暴力或者最小堆

    算法一,最小堆

    算法二,最大堆

    • 首先将堆调整为最大堆,堆顶元素为数组最大元素在A[0]位置,与A[N]交换位置。找到最大元素,将其放在最后。再调整为最大堆,且堆的大小-1,为N-1。依次执行。
    • 定理:堆排序处理N个不同元素的随机排列的平均比较次数是2N logN - O(Nlog logN) 。
    • 不稳定
    • 虽然堆排序给出最佳平均时间复杂度,但实际效果不如用Sedgewick增量序列的希尔排序。

    实现

    void PercDown(ElementType A[], int p, int N)
    { /* 改编代码4.24的PercDown( MaxHeap H, int p )    */
    	/* 将N个元素的数组中以A[p]为根的子堆调整为最大堆 */
    	int Parent, Child;
    	ElementType X;
    
    	X = A[p]; /* 取出根结点存放的值 */
    	for (Parent = p; (Parent * 2 + 1) < N; Parent = Child) {
    		Child = Parent * 2 + 1;
    		if ((Child != N - 1) && (A[Child] < A[Child + 1]))
    			Child++;  /* Child指向左右子结点的较大者 */
    		if (X >= A[Child]) break; /* 找到了合适位置 */
    		else  /* 下滤X */
    			A[Parent] = A[Child];
    	}
    	A[Parent] = X;
    }
    
    void Heap_Sort(ElementType A[], int N) //和堆的操作有点不一样:这里从0开始存储元素,堆的操作0位置为哨兵
    {
    	for (int i = N / 2 - 1; i >= 0; i--) /*buildMaxHeap建立最大堆*/
    	{
    		PercDown(A, i, N);
    	}
    
    	for (int i = N - 1; i > 0;i--)
    	{
    		/*删除最大堆顶*/
    		int temp = A[0];
    		A[0] = A[i];
    		A[i] = temp;
    		PercDown(A, 0, i); //长度减1,依次建立
    	}
    }
    
    

    应用

     找十个最小的,那你最简单的方法只要一个个比过去,用10N就好。
     1万个数里面找最大的10个数,也就说只需要一个大小为10的最小堆就行了。
     从第11个数开始,每次输入一个数,比堆顶大的话,就替换掉堆顶元素。
     这样1w个数下来,最小堆中就保存了最大的10个数字。
     复杂度是 1W * log(10), 或者说 N log(k) 其中 k是一个远比N小的常数
    

    归并排序

    • 归并排序一般不作内排序,在外排序中用

    • 核心:有序子列的归并

    • 稳定排序

    有序子列的归并实现

    /* L = 左边起始位置, R = 右边起始位置, RightEnd = 右边终点位置*/
    void Merge( ElementType A[], ElementType TmpA[], int L, int R, int RightEnd )
    { /* 将有序的A[L]~A[R-1]和A[R]~A[RightEnd]归并成一个有序序列 */
    
         int LeftEnd = R - 1; /* 左边终点位置 */
         int temp = L;         /* 有序序列的起始位置 */
         int NumElements = RightEnd - L + 1;
          
         while( L <= LeftEnd && R <= RightEnd ) {
             if ( A[L] <= A[R] )
                 TmpA[temp++] = A[L++]; /* 将左边元素复制到TmpA */
             else
                 TmpA[temp++] = A[R++]; /* 将右边元素复制到TmpA */
         }
     
         while( L <= LeftEnd )
             TmpA[temp++] = A[L++]; /* 直接复制左边剩下的 */
         while( R <= RightEnd )
             TmpA[temp++] = A[R++]; /* 直接复制右边剩下的 */
              
         for(int i = 0; i < NumElements; i++, RightEnd -- ) //细节处理
             A[RightEnd] = TmpA[RightEnd]; /* 将有序的TmpA[]复制回A[] */
    }
    
    

    递归算法

    void Msort( ElementType A[], ElementType TmpA[], int L, int RightEnd )
    { /* 核心递归排序函数 */ 
         int Center;
          
         if ( L < RightEnd ) {
              Center = (L+RightEnd) / 2;
              Msort( A, TmpA, L, Center );              /* 递归解决左边 */ 
              Msort( A, TmpA, Center+1, RightEnd );     /* 递归解决右边 */  
              Merge( A, TmpA, L, Center+1, RightEnd );  /* 合并两段有序序列 */ 
         }
    }
     
    void MergeSort( ElementType A[], int N )
    { /* 归并排序 */
         ElementType *TmpA;
         TmpA = (ElementType *)malloc(N*sizeof(ElementType));
          
         if ( TmpA != NULL ) {
              Msort( A, TmpA, 0, N-1 );
              free( TmpA );
         }
         else printf( "空间不足" );
    }
    

    非递归算法

    /* 归并排序 - 循环实现 */
    /* 这里Merge函数在递归版本中给出 */
     
    /* length = 当前有序子列的长度*/
    void Merge_pass( ElementType A[], ElementType TmpA[], int N, int length )
    { /* 两两归并相邻有序子列 */
         int i, j;
           
         for ( i=0; i <= N-2*length; i += 2*length )
             Merge( A, TmpA, i, i+length, i+2*length-1 );
         if ( i+length < N ) /* 归并最后2个子列*/
             Merge( A, TmpA, i, i+length, N-1);
         else /* 最后只剩1个子列*/
             for ( j = i; j < N; j++ ) TmpA[j] = A[j];
    }
     
    void Merge_Sort( ElementType A[], int N )
    { 
         int length; 
         ElementType *TmpA;
          
         length = 1; /* 初始化子序列长度*/
         TmpA = malloc( N * sizeof( ElementType ) );
         if ( TmpA != NULL ) {
              while( length < N ) {
                  Merge_pass( A, TmpA, N, length );
                  length *= 2;
                  Merge_pass( TmpA, A, N, length );
                  length *= 2;
              }
              free( TmpA );
         }
         else printf( "空间不足" );
    }
    
    

    Reference

  • 相关阅读:
    ASP.NET MVC基于标注特性的Model验证:DataAnnotationsModelValidator
    TDD个人实践体会
    客户端调用Spring.Net发布的WebService
    XML自动解析器开源
    Javascript MVVM模式前端框架—Knockout 2.1.0系列
    定时执行SQL存储过程
    orchard之lucene.net索引生成
    并发编程学习总结
    python开发总结
    Thrift
  • 原文地址:https://www.cnblogs.com/ranjiewen/p/6792320.html
Copyright © 2011-2022 走看看