zoukankan      html  css  js  c++  java
  • 数据结构学习笔记排序 (快速,计数排序,表排序,桶排序,基数排序)


    layout: post
    title: 数据结构学习笔记排序 (快速,表排序,基数排序)
    date: 2017-05-20
    tag: 数据结构和算法

    计数排序:
    该算法于1954年由 Harold H. Seward 提出。它是一个不需要比较的,类似于桶排序的线性时间排序算法。该算法是对已知数量范围的数组进行排序。其时间复杂度为O(n),适用于小范围集合的排序。计数排序是用来排序0到100之间的数字的最好的算法。比如100万学生参加高考,我们想对这100万学生的数学成绩(假设分数为0到100)做个排序。
    基本思想:对于给定的输入序列中的每一个元素x,确定该序列中值小于x的元素的个数 。一旦有了这个信息,就可以将x直接存放到最终的输出序列的正确位置上。它创建一个长度为这个数据范围的数组C,C中每个元素记录要排序数组中对应记录的出现个数。 
    
    算法的步骤如下:
    1.找出待排序的数组中最大和最小的元素
    2.统计数组中每个值为i的元素出现的次数,存入数组C的第i项
    3.对所有的计数累加(从C中的第一个元素开始,每一项和前一项相加)
    4.反向填充目标数组:将每个元素i放在新数组的第C(i)项,每放一个 元素就将C(i)减去1 
    

    目录

    • TOC
      {:toc}

    快速排序

    • 不稳排序
    • 采用分而治之思想
    • 最好的情况:每次主元正好中分,T(N)=O(NlogN)
    • 最坏情况:每次主元一边倒,T(N)=O(N*N)
    • 选主元 的方法有很多,这里用 取头、中、尾的中位数。

    • 快速排序的之所以快:在于每次对于主元一次就可以放到指定位置
    • 小规模数据的处理:
      • 快速排序的问题:用递归……
      • 对小规模的数据(例如N不到100)可能还不如插入排序快
      • 当递归的数据规模充分小,则停止递归,直接调用简单排序(例如插入排序)在程序中定义一个Cutoff的阈值

    Qsort实现

    • C:qsort();C:sort();当时自定义的数据结构时,需要自定义实现比较函数
    • 快速排序 - 直接调用库函数
    
    #include<stdio.h>
    #define ElementType int 
    void InsertionSort(ElementType A[], int N)
    {
    	int i;
    	for (int P = 1; P<N; P ++ )
    	{
    		ElementType temp = A[P];//取出未排序序列中的第一个元素
    		for (i = P; i >0 && A[i - 1] > temp; i--)
    		{
    			/* code */
    			A[i] = A[i - 1]; //依次与已排序序列中元素比较并右移
    		}
    		A[i] = temp;
    	}
    }
    
    //快速排序
    ElementType Median3(ElementType A[], int Left, int Right)
    {
    	int Center = (Left + Right) / 2;
    	int temp;
    	if (A[Left] > A[Center])
    	{
    		temp = A[Left];
    		A[Left] = A[Center];
    		A[Center] = temp;
    	}
    	if (A[Left] > A[Right])
    	{
    		temp = A[Left];
    		A[Left] = A[Right];
    		A[Center] = temp;
    	}
    	if (A[Center] > A[Right])
    	{
    		temp = A[Center];
    		A[Center] = A[Right];
    		A[Right] = temp;
    	}
    	/*此时A[Left]<=A[Center]<=A[Right]*/
    	temp = A[Center];
    	A[Center] = A[Right - 1];
    	A[Right - 1] = temp; //将基准Pivot藏到右边
    	//只需要考虑A[Left+1]...到A[Right-2]的元素
    	return A[Right - 1];
    }
    
    void Qsort(ElementType A[], int Left, int Right)
    {
    	/*核心递归函数*/
    	int Pivot, CutOff=1000, Low, High;
    	int temp;
    	if (CutOff <= Right - Left)
    	{ //如果序列元素充分多,进入快排
    		Pivot = Median3(A, Left, Right); //选基准,返回中位数
    		Low = Left;
    		High = Right - 1;
    		while (1) //将序列中比基准小的移动到基准左边,大的移动到右边
    		{
    			while (A[++Low]<Pivot);
    			while (A[--High]>Pivot);
    			if (Low < High)
    			{
    				temp = A[Low];
    				A[Low] = A[High];
    				A[High] = temp;
    			}
    			else
    				break;
    		}
    		temp = A[Low];
    		A[Low] = A[Right - 1];
    		A[Right - 1] = temp; //将基准换到正确的位置,快速排序之所以快,就在于一次性将元素的位置放到正确的位置
    
    		Qsort(A, Left, Low - 1); //递归解决左边
    		Qsort(A, Low + 1, Right); //递归解决右边	
    	}
    	else
    	{ 
    		InsertionSort( A+Left, Right-Left+1);  //元素太少,用简单排序
    	}
    }
    
    void QuickSort(ElementType A[], int N)
    {
    	Qsort(A, 0, N - 1); //统一接口,排序子问题的左右下标
    }
    
    int main(int argc, char const *argv[])
    {
    	int a[] = { 34, 8, 64, 51, 32, 21 };
    	QuickSort(a, 6);
    	for (int i = 0; i < sizeof(a) / sizeof(int); ++i)
    	{
    		/* code */
    		printf("%d ", a[i]);
    	}
    	return 0;
    }
    
    
    
    • 用的较多的快速排序
    //快速排序 O(N*log(N))
    #include <math.h>
    class QuickSort {
    public:
        int* quickSort(int* A, int n) {
            // write code here
            quickSort(A, 0, n - 1);
            return A;
        }
        void quickSort(int* A, int low, int high)
        {
            if (low <= high)
            {
                int part = partition(A, low, high);
                quickSort(A, low, part - 1);
                quickSort(A, part + 1, high);
            }
            return;
        }
    
        int partition(int* A, int low, int high)
        {
            int privotKey = A[low];    //基准元素
            while (low < high)
            {        //从表的两端交替地向中间扫描
                while (low < high  && A[high] >= privotKey) 
                    --high;  //从high 所指位置向前搜索,至多到low+1 位置。将比基准元素小的交换到低端
                swap(&A[low], &A[high]);
                while (low < high  && A[low] <= privotKey) 
                    ++low;
                swap(&A[low], &A[high]);
            }
            return low;
        }
    };
    
    

    快速排序的非递归实现

    • 主要是将第一层每个子序列的左右边界用栈存储起来,然后在一层取出边界,再做partition,再存储下一层的边界
        void quickSort2(double* a, int left, int right)  
        {  
            stack<int> t;  
            if(left<right)  
            {  
                int p = partition(a, left, right);  
          
                if (p-1>left)  
                {  
                    t.push(left);  
                    t.push(p-1);  
                }  
                if (p+1<right)  
                {  
                    t.push(p+1);  
                    t.push(right);  
                }  
          
                while(!t.empty())  
                {  
                    int r = t.top();  
                    t.pop();  
                    int l = t.top();  
                    t.pop();  
          
                    p = partition(a, l, r);  
          
                    if (p-1>l)  
                    {  
                        t.push(l);  
                        t.push(p-1);  
                    }  
                    if (p+1<r)  
                    {  
                        t.push(p+1);  
                        t.push(r);  
                    }  
          
                }  
            }  
        }  
    

    表排序

    • 表排序适合就是数据本身容量大,交换代价比较大,我们只需要对其下标进行操作

    基数排序

    桶排序

    • N>>M时,复杂度:T(N,M)=O(M+N);M>>N桶排序不合理

    基数排序

    • M>>N时,T(N)=O(P(N+B)),B->Bucket,P->趟数

    • LSD(Least Significant Digit)次为优先
    • MSD(Most Significant Digit)主位优先
    • LSD和MSD适合不同的场合,速度不一样

    LSD基数排序实现

    #include<stdio.h>
    #include <stdlib.h>
    
    typedef int ElementType;
    //基数排序,次为优先
    
    //假设元素最多有MaxDigit个关键字,基数全部是同样的Tadix
    #define MaxDigit 4
    #define  Radix 10
    
    //桶元素节点
    typedef struct Node *PtrToNode;
    struct Node
    {
    	int key;
    	PtrToNode next;
    };
    
    //桶头节点
    struct HeadNode
    {
    	PtrToNode head, tail;
    };
    
    typedef struct HeadNode Bucket[Radix];
    
    int GetDigit(int X, int D)
    {
    	//默认次位D=1,主位D<=MaxDigit
    	int d, i;
    	for (i = 1; i <= D;i++)
    	{
    		d = X%Radix;
    		X /= Radix;
    	}
    	return d;
    }
    
    int LSDRadixSort(ElementType A[], int N)
    {
    	int D, Di, i;
    	Bucket B;
    	PtrToNode temp, p, List = NULL;
    
    	for (i = 0; i < Radix;i++) /* 初始化每个桶为空链表 */
    	{
    		B[i].head = B[i].tail = NULL;
    	}
    
    	for (i = 0; i < N; i++)   /* 将原始序列逆序存入初始链表List */
    	{
    		temp = (PtrToNode)malloc(sizeof(struct Node));
    		temp->key = A[i];
    		temp->next = List;
    		List = temp;
    	}
    	for (D = 1; D <= MaxDigit;D++) /* 对数据的每一位循环处理 */
    	{  /* 下面是分配的过程 */
    		p = List;
    		while (p)
    		{
    			Di = GetDigit(p->key, D); //获取当前元素的当前位数字
    			//从List中摘除
    			temp = p;
    			p = p->next;
    			//插入B[Di]号的桶尾
    			temp->next = NULL;
    			if (B[Di].head==NULL)
    			{
    				B[Di].head = B[Di].tail = temp;
    			}
    			else
    			{
    				B[Di].tail->next = temp;
    				B[Di].tail = temp;
    			}
    		}
    		//下面是收集的过程
    		List = NULL;
    		for (Di = Radix - 1; Di >= 0;Di--)  /* 将每个桶的元素顺序收集入List */
    		{
    			if (B[Di].head)  //如果桶不为空
    			{
    				//整桶插入List表头中
    				B[Di].tail->next = List;
    				List = B[Di].head;
    				B[Di].head = B[Di].tail = NULL; //清空桶
    			}
    		}
    	}
    	//将List倒入A[]并释放空间
    	for (i = 0; i < N;i++)
    	{
    		temp = List;
    		List = List->next;
    		A[i] = temp->key;
    		free(temp);
    	}
    	return 0;
    }
    
    int main()
    {
    	int a[] = {34,8,64,51,32,21};
    	LSDRadixSort(a,6);
    	for (int i = 0; i < 6;i++)
    	{
    		printf("%d ", a[i]);
    	}
    	return 0;
    }
    
    

    MSD基数排序实现

    #include <stdio.h> 
    #include <stdlib.h>
    
    typedef int ElementType;
    
    /* 基数排序 - 主位优先 */
     
    /* 假设元素最多有MaxDigit个关键字,基数全是同样的Radix */
     
    #define MaxDigit 4
    #define Radix 10
     
    /* 桶元素结点 */
    typedef struct Node *PtrToNode;
    struct Node{
        int key;
        PtrToNode next;
    };
     
    /* 桶头结点 */
    struct HeadNode {
        PtrToNode head, tail;
    };
    typedef struct HeadNode Bucket[Radix];
      
    int GetDigit ( int X, int D )
    { /* 默认次位D=1, 主位D<=MaxDigit */
        int d, i;
         
        for (i=1; i<=D; i++) {
            d = X%Radix;
            X /= Radix;
        }
        return d;
    }
     
    void MSD( ElementType A[], int L, int R, int D )
    { /* 核心递归函数: 对A[L]...A[R]的第D位数进行排序 */
         int Di, i, j;
         Bucket B;
         PtrToNode tmp, p, List = NULL; 
         if (D==0) return; /* 递归终止条件 */
          
         for (i=0; i<Radix; i++) /* 初始化每个桶为空链表 */
             B[i].head = B[i].tail = NULL;
         for (i=L; i<=R; i++) { /* 将原始序列逆序存入初始链表List */
             tmp = (PtrToNode)malloc(sizeof(struct Node));
             tmp->key = A[i];
             tmp->next = List;
             List = tmp;
         }
         /* 下面是分配的过程 */
         p = List;
         while (p) {
             Di = GetDigit(p->key, D); /* 获得当前元素的当前位数字 */
             /* 从List中摘除 */
             tmp = p; p = p->next;
             /* 插入B[Di]号桶 */
             if (B[Di].head == NULL) B[Di].tail = tmp;
             tmp->next = B[Di].head;
             B[Di].head = tmp;
         }
         /* 下面是收集的过程 */
         i = j = L; /* i, j记录当前要处理的A[]的左右端下标 */
         for (Di=0; Di<Radix; Di++) { /* 对于每个桶 */
             if (B[Di].head) { /* 将非空的桶整桶倒入A[], 递归排序 */
                 p = B[Di].head;
                 while (p) {
                     tmp = p;
                     p = p->next;
                     A[j++] = tmp->key;
                     free(tmp);
                 }
                 /* 递归对该桶数据排序, 位数减1 */
                 MSD(A, i, j-1, D-1);
                 i = j; /* 为下一个桶对应的A[]左端 */
             } 
         } 
    }
     
    void MSDRadixSort( ElementType A[], int N )
    { /* 统一接口 */
        MSD(A, 0, N-1, MaxDigit); 
    }
    
    int main()
    {
        int a[] = {34,8,64,51,32,21};
        MSDRadixSort(a, 6);
        for(int i = 0; i < 6; i++)
            printf("%d ",a[i]);
        return 0;
    }
    
    
    • 多关键排序时候,先MSD,在LSD

    总结

    Reference

  • 相关阅读:
    php图片水印添加,压缩,剪切的封装类
    使用观察者模式处理异常信息
    php中的错误级别
    php 递归函数的三种实现方式
    php利用递归函数实现无限级分类
    利用http协议发布博客园博文评论
    结合php ob函数理解缓冲机制
    php 正则表达式捕获组与非捕获组
    php 利用socket发送GET,POST请求
    php mysqli扩展之预处理
  • 原文地址:https://www.cnblogs.com/ranjiewen/p/6882763.html
Copyright © 2011-2022 走看看