您的位置:首页 > 理论基础 > 数据结构算法

数据结构学习笔记排序 (快速,表排序,基数排序)

2017-05-20 18:01 246 查看
layout: post

title: 数据结构学习笔记排序 (快速,表排序,基数排序)

date: 2017-05-20

tag: 数据结构和算法

---

目录

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;
}
};

表排序

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



学习地址视频

基数排序

桶排序

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

排序算法的实现(归并,快排,堆排,希尔排序 O(N*log(N)))
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐