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

  • 相关阅读:
    单例模式
    HashSet、LinkedHashSet、SortedSet、TreeSet
    ArrayList、LinkedList、CopyOnWriteArrayList
    HashMap、Hashtable、LinkedHashMap
    andrew ng machine learning week8 非监督学习
    andrew ng machine learning week7 支持向量机
    andrew ng machine learning week6 机器学习算法理论
    andrew ng machine learning week5 神经网络
    andrew ng machine learning week4 神经网络
    vue组件监听属性变化watch方法报[Vue warn]: Method "watch" has type "object" in the component definition. Did you reference the function correctly?
  • 原文地址:https://www.cnblogs.com/ranjiewen/p/6792320.html
Copyright © 2011-2022 走看看