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

数据结构 28 排序 计数排序 基数排序 桶排序

2014-01-03 20:57 671 查看
1.计数排序

2.基数排序

3.桶排序

1.计数排序 使用与100内数排序

首先从计数排序(Counting Sort)开始介绍起,假设我们有一个待排序的整数序列A,其中元素的最小值不小于0,最大值不超过K。建立一个长度为K的线性表C,用来记录不大于每个值的元素的个数。

算法思路:

1.找出待排序的数组中最大和最小的元素

2.统计数组中每个值为i的元素出现的次数,存入数组Count_arr的第i项

3.对所有的计数累加(从Count_arr中的第一个元素开始,每一项和前一项相加)

4.反向遍历原数组:将每个元素i放在新数组的第Count_arr(i)项,每放一个元素就将Count_arr(i)减去1





#include <stdio.h>
#include <stdlib.h>
#include <malloc.h>
#include <string.h>

/**************************************************************
*计数排序。
*参数: data : 要排序的数组
*       size :数组元素的个数
*       k    :数组中元素数组最大值 +1 (这个需要+1)
*返回值: 成功0;失败-1.
* ************************************************************/

int cnt_sort(int* data, int size, int k)
{
int* counts = NULL; /*计数数组*/
int* temp = NULL;   /*保存排序后的数组*/

if ((counts = (int *) malloc( k * sizeof(int))) == NULL)   /*申请数组空间*/
return -1;
if ((temp = (int *) malloc( k * sizeof(int))) == NULL)
return -1;

for (int i = 0; i < k; i ++)   /*初始化计数数组*/
{
counts[i] = 0;
}

for(int i = 0; i < size; i++)  /*数组中出现的元素,及出现次数记录*/
{
counts[data[i]] += 1;
}

for (i = 1; i < k; i++)      /*调整元素计数中,加上前一个数*/
{
counts[i] += counts[i - 1];
}

for (i = size -1; i >= 0; i --)/*使用计数数组中的记录数值,来进行排序,排序后保存的temp*/
{
temp[counts[data[i]] - 1] = data[i];
counts[data[i]] -= 1;
}

memcpy(data,temp,size * sizeof(int));
free(counts);
free(temp);

return 0;
}

int main()
{
int a[] = {1,0,2,1,4,5,7,4};
int size = sizeof(a) / sizeof(a[o]);
int max = a[0];

for(int i = 0; i < size; ++i) // 1. find max in a[]
{
if(a[i] > max)
{
max = a[i];
}
}

cnt_sort(a, len, max + 1); // 由于有0所以需要加1

for(int i = 0; i < size; ++i)
{
cout<<a[i]<<endl;
}

return 0;
}


12.基数排序
将所有待比较数值(正整数)统一为同样的数位长度,数位较短的数前面补零. 然后, 从最低位开始, 依次进行一次排序.这样从最低位排序一直到最高位排序完成以后, 数列就变成一个有序序列.
基数排序的方式可以采用LSD(Least significant digital)或MSD(Most significant digital),LSD的排序方式由键值的最右边开始,而MSD则相反,由键值的最左边开始。
实现:



#include<iostream>
#include<vector>
#include<queue>
using namespace std;

// 使用的方法很像hash查找 都用到了桶

// 1. 将vector中的数按照第power位 推入到队列中  这里的deq是一个数组 类似于hash
void VecToDeque(vector<int>&vec,queue<int> deq[],int power)
{
vector<int>::iterator it;
for(it=vec.begin();it!=vec.end();it++)
{
int itor=(*it) / power % 10;  // 例如: power = 10 时 132 /10 -> 13 % 10 -> 3 这样将*it压入deq中的第3个位置 对应的队列 power取10 100 1000...
deq[itor].push(*it);          // 取余的结果是几 便放入第几个位置
// 如果遇到两个数的某一位相同则加入相同的队列
}
}
// 2. 将队列中按照某位 排好的数拷贝到容器中
void DeqToVec(vector<int>&vect,queue<int>deq[])
{
int count=0;
for(int i=0;i<10;i++)             // 队列组
{
while(!deq[i].empty())        // 具体的每个队列 由于可能有某位相同的几个数加入了同一个队列所以需要变量某个队列将其拷贝到vector中
{
vect[count]=deq[i].front();
count++;
deq[i].pop();  // 出队列 同时将front指向下一个元素
}
}
}

int Max(vector<int>&vec)  //求队列中的最大数的位数
{

vector<int>::iterator it;
it=vec.begin();
int max=*it;
for(it=vec.begin();it!=vec.end();it++)  // 得到最大数
{
if(max<*it)
{
max=*it;
}
}
int count=1;
while(max>=10)  // 得到最大位数
{
max/=10;
count++;
}
return
count;
}
void RadixSort(vector<int>&vec)  // 3.调用VecToDeque和Colect对数字进行排序

{
int count=Max(vec);
int power=1;
queue<int>que[10];

for(int i=1;i<=count;i++)
{
VecToDeque(vec,que,power);  // 每次执行这两部操作将vector中元素按照取位放入deque中 再将某位排好的拷贝到vector中 完成按照某位的一次排序 这里power作为队列组的索引
DeqToVec(vec,que);
power*=10;
}
}
int main()
{
vector<int>vec;                 // 当数据很大时用链表或者动态环形队列来存储 这是的vector实际就是一个动态环形队列

for(int i=0;i<10;i++)           //产生随机数
{
vec.push_back(rand()%99);   // 这里没有考虑负数
}

vector<int>::iterator it;
for(it=vec.begin();it!=vec.end();it++)
{
cout<<*it<<" ";
}
cout<<endl;

RadixSort(vec);
for(it=vec.begin();it!=vec.end();it++)
{
cout<<*it<<" ";
}
return 0;
}


周伟明的写法(用C++实现关注于算法更简洁 好理解 对于C语言 没有vector可以考虑用链表代替)

/** 对链表的数据的第uKeyIndex位上的元素进行分类,
依照它们的大小放入对应的箱子中

@param  SINGLELIST *pSingleList - 单向链表指针
@param  UINT       uRadix - 基数排序的基数,与具体数据类型有关,
一般来讲整数的基数为16,字符串的基数最大为255。
@param  UINT       uKeyIndex - 第多少位
@param  SINGLENODE **ppHead - 用来记录头指针的箱子
@param  SINGLENODE **ppTail - 记录箱子的尾指针
@param  GETKEYFUNC GetKeyFunc - 获取数据的第uKeyIndex位上的元素值
@return void - 无
*/
static void SingleList_Distribute(SINGLELIST *pSingleList,
UINT       uRadix,
UINT       uKeyIndex,
SINGLENODE **ppHead,
SINGLENODE **ppTail,
GETKEYFUNC GetKeyFunc )
{
SINGLENODE   *pNode;
UINT         i;

/* 初始化子表 */
for ( i = 0; i < uRadix; i++ )
{
ppHead[i] = NULL;
ppTail[i] = NULL;
}

pNode = pSingleList->pHead;

while ( pNode != NULL )
{
UINT uRadixIndex = (*GetKeyFunc)(pNode->pData, uKeyIndex);

if ( ppHead[uRadixIndex] == NULL )
{
ppHead[uRadixIndex] = pNode;
ppTail[uRadixIndex] = pNode;
pNode = pNode->pNext;
ppTail[uRadixIndex]->pNext = NULL;
}
else
{
ppTail[uRadixIndex]->pNext = pNode;
ppTail[uRadixIndex] = ppTail[uRadixIndex]->pNext;
pNode = pNode->pNext;
ppTail[uRadixIndex]->pNext = NULL;
}
}
}

/** 对基数排序中分好类的箱子进行收集操作,将箱子重新连成一条链表

@param  SINGLELIST *pSingleList - 单向链表指针
@param  UINT        uRadix - 基数
@param  SINGLENODE **ppHead - 用来记录头指针的箱子
@param  SINGLENODE **ppTail - 记录箱子的尾指针
@return void - 无。
*/
static void SingleList_Collect(SINGLELIST *pSingleList,
UINT        uRadix,
SINGLENODE **ppHead,
SINGLENODE **ppTail )
{
SINGLENODE  *pHead;
SINGLENODE  *pTail;
UINT        uRadixIndex;

/* 查早第1个非空子表 */
uRadixIndex = 0;
while ( uRadixIndex < uRadix )
{
if ( ppHead[uRadixIndex] == NULL )
{
uRadixIndex++;
continue;
}
else
{
break;
}
}

if ( uRadixIndex == uRadix )
{
/* 没有找到非空子表 */
return ;
}

pHead = ppHead[uRadixIndex];
pTail = ppTail[uRadixIndex];

while ( uRadixIndex < uRadix - 1 )
{
/* 继续查找下一个非空子表 */
++uRadixIndex;
if ( ppHead[uRadixIndex] == NULL )
{
continue;
}

if ( uRadixIndex < uRadix )
{
/* 找到了非空子表,需要把它和前一个非空子表链接起来 */
pTail->pNext = ppHead[uRadixIndex];
pTail = ppTail[uRadixIndex];
}
}

pSingleList->pHead = pHead;
pSingleList->pTail = pTail;
}

/** 单向链表的基数排序函数

@param  SINGLELIST *pSingleList - 单向链表指针
@param  UINT uRadix - 基数,字符串如果以单字节为基的话基数为256
整数以10进制计算基数的话,基数为10
@param  UINT uMaxKeyLen - 关键词的长度,字符串以字节为单位则长度为字符串
本身最大可能长度,如果32位整数以16进制为单位的
话,则最大长度为8
@param  GETKEYFUNC GetKeyFunc - 关键词获取回调函数
@return INT - CAPI_FAILED表示失败,CAPI_SUCCESS表示成功。
*/
INT SingleList_RadixSort( SINGLELIST *pSingleList,
UINT uRadix,
UINT uMaxKeyLen,
GETKEYFUNC GetKeyFunc )
{
SINGLENODE  **ppHead;  /* 用来记录各个箱子头节点的双指针 */
SINGLENODE  **ppTail;  /* 用来记录各个箱子尾节点的双指针 */
UINT        i;         /* 临时循环变量 */

/* 给箱子申请内存 */
ppHead = (SINGLENODE **)malloc( uRadix * sizeof(SINGLENODE *) );
ppTail = (SINGLENODE **)malloc( uRadix * sizeof(SINGLENODE *) );
if ( ppHead == NULL || ppTail == NULL )
{
return CAPI_FAILED;
}

/* 按顺序对关键字的第i位进行分配和收集操作 */
for ( i = 0; i < uMaxKeyLen; i++ )
{
SingleList_Distribute(pSingleList, uRadix, i, ppHead, ppTail, GetKeyFunc);

SingleList_Collect(pSingleList, uRadix, ppHead, ppTail);
}

/* 释放分配的箱子 */
free( ppHead );
free( ppTail );

return CAPI_SUCCESS;
}

/** 获取字符串的第uKeyIndex位置上的字符
获取到的字符会自动转换成大写形式。

@param  void *pszData - 指向字符串的指针
@param  UINT uKeyIndex - 表示第多少位,位置是从后向前算
@return UINT - 返回第uKeyIndex位的字符值
*/
UINT GetStrKeyNoCase( void *pszData, UINT uKeyIndex )
{
char *psz = (char *)pszData;
UINT    uKey;

if ( psz == NULL || (*psz) == '\0' )
{
return 0;
}
if ( uKeyIndex >= strlen(psz) )
{
return 0;
}

uKey = strlen(psz) - uKeyIndex - 1;

if( psz[uKey] >= 'a' && psz[uKey] <= 'z' )
{
uKey = (UINT)(unsigned char)(psz[uKey] - 'a' + 'A');
}
else
{
uKey = (UINT)(unsigned char)(psz[uKey]);
}

return uKey;
}

/** 获取字符串的第uKeyIndex位置上的字符
获取到的字符区分大小写,保持原值不变。

@param  void *pszData - 指向字符串的指针
@param  UINT uKeyIndex - 表示第多少位,位置是从后向前算
@return UINT - 返回第uKeyIndex位的字符值
*/
UINT GetStrKey( void *pszData, UINT uKeyIndex )
{
char *psz = (char *)pszData;
UINT    uKey;

if ( psz == NULL || (*psz) == '\0' )
{
return 0;
}
if ( uKeyIndex >= strlen(psz) )
{
return 0;
}

uKey = strlen(psz) - uKeyIndex - 1;

uKey = (UINT)(unsigned char)(psz[uKey]);

return uKey;
}

/** 获取整数的第uKeyIndex位数字,以16进制为单位
因此获取到的数字最大为0xf,对整数作基数排序时,
基数为16,最大关键词长度为8

@param  void *pData - 整数值
@param  UINT uKeyIndex - 整数的第多少位
@return UINT - 返回第uKeyIndex位的数字
*/
UINT GetIntKey( void *pData, UINT uKeyIndex )
{
UINT    uData;

if ( uKeyIndex > 8 )
{
return 0;
}

uData = (UINT)pData;

uData = (uData >> (uKeyIndex - 1)) & 0xf;

return uData;
}


13.桶排序

桶排序:将数组分到有限个桶子内,然后再对桶子里面的序列进行排序,运行时间Θ(n)。桶排序基于一个假设:输入的数据由随机过程构成,否则在最坏情况下都分配到一个桶子里面,如果又不满足计数排序的假设要求,那么只能使用基于比较的排序算法进行排序,运行时间就退化到Ω(nlogn)。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: