第二弹(C++)!!!
以下排序均考虑正负数。。。代码写得好难看
接着上次说~
这次我主要写思想和放代码。。。
- 希尔排序
- 鸡尾酒/双向冒泡排序
- 堆排序
- 桶排序
- 基数排序
Talk is cheap, show you the code!!!
希尔排序
对于直接插入排序问题,数据量巨大时。采用希尔排序。。。
1. 将数的个数设为n,取奇数k=n/2,将下标差值为k的书分为一组,构成有序序列。
2. 再取k=k/2 ,将下标差值为k的书分为一组,构成有序序列。
3. 重复第二步,直到k=1执行简单插入排序。
//希尔排序
template <typename T>
void Shell_Sort(T *arrayT, size_t length)
{
int d = length;
while (d!=0)
{
d=d/2;
for (int x = 0; x < d; x++) //分的组数
{
for (int i = x + d; i < length; i += d) //组中的元素,从第二个数开始
{
int j = i - d;//j为有序序列最后一位的位数
int temp = arrayT[i];//要插入的元素
while(j >= 0 && temp < arrayT[j])//从后往前遍历。
{
arrayT[j + d] = arrayT[j];//向后移动d位
j -= d;
}
arrayT[j + d] = temp;
}
}
}
}
鸡尾酒排序-双向冒泡排序
鸡尾酒排序等于是冒泡排序的轻微变形。不同的地方在于从低到高然后从高到低,而冒泡排序则仅从低到高去比较序列里的每个元素。他可以得到比冒泡排序稍微好一点的效能,原因是冒泡排序只从一个方向进行比对(由低到高),每次循环只移动一个项目。
1、依次比较相邻的两个数,将小数放在前面,大数放在后面;
2、第一趟可得到:将最小数放到第一位。
3、第二趟可得到:将最大的数放到最后一位。
4、如此下去,重复以上过程,直至最终完成排序。
鸡尾酒排序最糟或是平均所花费的次数都是O(n^2),但如果序列在一开始已经大部分排序过的话,会接近O(n)。
最差时间复杂度 O(n^2)
最优时间复杂度 O(n)
平均时间复杂度 O(n^2)
//鸡尾酒排序--双向冒泡排序
template <typename T>
void Cocktail_Sort(T *arrayT, size_t length)
{
int tail=length-1;
int temp=0;
for (int i=0; i<tail;)
{
for (int j=tail; j>i; --j) //第一轮,先将最小的数据排到前面
{
if (arrayT[j]<arrayT[j-1])
{
temp=arrayT[j];
arrayT[j]=arrayT[j-1];
arrayT[j-1]=temp;
}
}
++i; //原来i处数据已排好序,加1
for (int j=i; j<tail; ++j) //第二轮,将最大的数据排到后面
{
if (arrayT[j]>arrayT[j+1])
{
temp=arrayT[j];
arrayT[j]=arrayT[j+1];
arrayT[j+1]=temp;
}
}
tail--; //原tail处数据也已排好序,将其减1
}
}
堆排序
我们这里提到的堆一般都指的是二叉堆,它满足二个特性:
1—父结点的键值总是大于或等于(小于或等于)任何一个子节点的键值。
2—每个结点的左子树和右子树都是一个二叉堆(都是最大堆或最小堆)。
如下为一个最小堆(父结点的键值总是小于任何一个子节点的键值)
堆调整(Heap Adjust):这是为了保持堆的特性而做的一个操作。对某一个节点为根的子树做堆调整,其实就是将该根节点进行“下沉”操作(具体是通过和子节点交换完成的),一直下沉到合适的位置,使得刚才的子树满足堆的性质。
例如对最大堆的堆调整我们会这么做:
1、在对应的数组元素A[i], 左孩子A[LEFT(i)], 和右孩子A[RIGHT(i)]中找到最大的那一个,将其下标存储在largest中。
2、如果A[i]已经就是最大的元素,则程序直接结束。
3、否则,i的某个子结点为最大的元素,将A[largest]与A[i]交换。
4、再从交换的子节点开始,重复1,2,3步,直至叶子节点,算完成一次堆调整。
这里需要提一下的是,一般做一次堆调整的时间复杂度为log(n)。
//------------------------------堆排序
/*返回父节点*/
inline int parent(int i)
{
return (int)floor((i - 1) / 2);
}
/*返回左孩子节点*/
inline int left(int i)
{
return (2 * i + 1);
}
/*返回右孩子节点*/
inline int right(int i)
{
return (2 * i + 2);
}
/*对以某一节点为根的子树做堆调整(保证最大堆性质)*/
template <typename T>
void HeapAdjust(T A[], int i, int heap_size)
{
int l = left(i);
int r = right(i);
int largest;
int temp;
//左孩子 层间顺序调动
if(l < heap_size && A[l] > A[i])
{
largest = l;
}
else
{
largest = i;
}
//右孩子 层内顺序调动
if(r < heap_size && A[r] > A[largest])
{
largest = r;
}
//是否交换
if(largest != i)
{
temp = A[i];
A[i] = A[largest];
A[largest] = temp;
HeapAdjust(A, largest, heap_size);
}
}
/*建立最大堆*/
template <typename T>
void BuildHeap(T A[],int heap_size)
{
//倒着建堆
for(int i = (heap_size-2)/2; i >= 0; i--)
{
HeapAdjust(A, i, heap_size);
}
}
/*堆排序*/
template <typename T>
void HeapSort(T A[], int heap_size)
{
BuildHeap(A, heap_size);
int temp;
//因为是大根堆,所以A[0]是堆顶元素是最大的,倒着放在最后,这样就是从小到大
for(int i = heap_size - 1; i >= 0; i--)
{
temp = A[0];
A[0] = A[i];
A[i] = temp;
HeapAdjust(A, 0, i);//找到当前的最大值
}
}
桶排序
(数有多大桶就有多大,好痛苦)
桶排序的思想就是这里有一个数量为Size个数的数组A,数组的值范围为(0 - Max)
这样我们可以创建一个大小为Max+1的数组B,每个元素都为0.
从头遍历A,当读取到A[i]的时候,B[A[i]]的值+1,这样所有的A数组被遍历后,直接扫描B之后,输出表B就可以了。
同理,为负数建立一个数组,然后倒着查找即可实现整数的排序。
//桶排序
template <typename T>
void Bucket_Sort(T *A, int Max, int Size)
{
int B[Max+1];//正数的桶
int M[Max+1];//负数的桶
int i,j,count = 0;
//初始化
for (int k = 0; k <= Max; ++k)
{
B[k] = 0;
M[k] = 0;
}
//计数
for (i = 0; i < Size; ++i)
{
j = A[i];
if(j>=0)
{
B[j] += 1;
}
else
{
j=-j;
M[j] += 1;
}
}
//排序后的数组
//负数
for (i = Max; i >0; --i)
{
if (M[i] > 0)
{
for (j = 0; j < M[i]; ++j)//输出个数
{
A[count] = -i;
count++;
}
}
}
//正数
for (i = 0; i <= Max; ++i)
{
if (B[i] > 0)
{
for (j = 0; j < B[i]; ++j)
{
A[count] = i;
count++;
}
}
}
}
基数排序
基数排序是一种非比较型整数排序算法,其原理是将整数按位数切割成不同的数字,然后按每个位数分别比较。由于整数也可以表达字符串(比如名字或日期)和特定格式的浮点数,所以基数排序也不是只能使用于整数。
1、将所有待比较数值(正整数)统一为同样的数位长度,数位较短的数前面补零。
2、从最低位开始,依次进行一次排序。
3、这样从最低位排序一直到最高位排序完成以后, 数列就变成一个有序序列。
//基数排序
template <typename T>
void display1D(vector<T> &result)
{
typename vector<T>::iterator iter;
for(iter=result.begin(); iter!=result.end(); ++iter)
{
cout<<(*iter)<<"\t";
}
cout<<endl;
}
//输出内容
template <typename T>
void display2D(vector<vector<T>> &result)
{
//使用迭代器
typename vector<vector<T>>::iterator iterAll;
typename vector<T>::iterator iter;
for( iterAll=result.begin(); iterAll!=result.end(); ++iterAll)
{
for(iter=(*iterAll).begin(); iter!=(*iterAll).end(); ++iter)
{
cout<<(*iter)<<"\t";
}
cout<<endl;
}
/* 已知数组容量
for( int i=0; i<result.size(); ++i)
{
for(int j=0; j<result.at(i).size(); ++j)
{
cout<<result.at(i).at(j)<<"\t";
}
cout<<endl;
}
*/
}
//找到最大的数值
template <typename T>
int findMax(vector<T> &result)
{
typename vector<T>::iterator iter=result.begin();
int temp=(*iter);
for(iter=result.begin(); iter!=result.end(); ++iter)
{
if(temp<(*iter))
{
temp=(*iter);
}
}
return temp;
}
//排序的次数
int getNumberOfSort(int number,int radix)
{
int counter=0;//初始化
while(number/radix)
{
counter++;
number/=radix;
}
return counter;
}
//一个二维的向量初始化
template <typename T>
void initialRadixVector(vector<vector<T>> &vectorAll,int radix)
{
for(int i=0; i<radix; ++i)
{
vector<T> temp;
vectorAll.push_back(temp);
}
}
//源复制到目的向量中
template <typename T>
void getVector(vector<T> &dest,vector<vector<T>> &source)
{
dest.clear();//清空源向量
typename vector<vector<T>>::iterator iterAll;
typename vector<T>::iterator iter;
typename vector<T>::reverse_iterator riter;
//遍历得到新的向量
for( iterAll=source.begin(); iterAll!=source.end(); ++iterAll)
{
vector<int> temp;
bool isMinus=false;
for(iter=(*iterAll).begin(); iter!=(*iterAll).end(); ++iter)
{
if(*iter>=0)
{
dest.push_back(*iter);
}
else
{
isMinus=true;
temp.push_back(*iter);
}
}
if(isMinus)//负数需要倒着输出
{
for(riter=temp.rbegin(); riter!=temp.rend(); ++riter)
{
dest.insert(dest.begin(),*riter);
}
temp.clear();
}
}
}
//向量放在各个基数内
template <typename T>
void SetRadix(vector<vector<T>> &vectorAll,vector<T> &vect,int number,int radix)
{
int location=0;//记录位置
typename vector<vector<T>>::iterator iterAll;
typename vector<T>::iterator iter;
for(iter=vect.begin(); iter!=vect.end(); ++iter)
{
location = abs(static_cast<int>(abs(*iter))/pow(radix,number))%(radix);
vectorAll.at(location).push_back(*iter);
}
}
//基数排序 --- 一次排序的过程
template <typename T>
void radixSortOnce(vector<T> &vect, int number,int radix)
{
//初始化向量
vector<vector<T>> vectorAll;//基数排序的基
//初始化向量表
initialRadixVector(vectorAll,radix);
//将向量放在各个基数内
SetRadix(vectorAll,vect,number,radix);
//复制到临时变量中
getVector(vect,vectorAll);
//显示每次排序的过程
//display1D(vect);
//cout<<endl;
}
//基数排序
template <typename T>
void radixSort(vector<T> &vect, int number,int radix)
{
if(0<=number)
{
radixSort(vect,number-1,radix);
radixSortOnce(vect,number,radix);
}// end of if
}
全文的代码放在这里了大家来找BUG呀!
戳我—带你去整个代码的地方,欢迎来给我找BUG
测试环境 CodeBlocks 16.01
Reading List
寒小阳
JS家的排序算法
一遍记住Java常用的八种排序算法与代码实现