冒泡排序
冒泡排序的基本原理: 依次比较相邻的两个数,将大数放后面(右边),小数放在前面(左边)。
即首先比较第1个和第2个数,将大数放后(右),小数放前(左)。然后比较第2个数和第3个数,将大数放后(右),
小数放前(左),如此继续,直至比较最后两个数。此时第1轮结束,如此在第1轮之后位于最前的数必是所有数
最小的。然后使用上1轮的结果重复以上过程,将所有的最小数放到最前。到所有的数都已经有序时算法结束。
选择排序从需要排序的队列数据中选择最小的同第一个值交换,再从剩下的部分中选择最小的与第二个交换,
这样往复下去,最后实现全队列的排序。
其基本思想是:把待排序的纪录按其关键码值的大小逐个插入到一个已经排好序的有序序列中,直到所有的
纪录插入完为止,得到一个新的有序序列。
直接插入算法把要排序的数组分成两部分
步骤大概是这样的:
1.从第一个元素开始,该元素可以认为已经被排序 代码中 iTemp = pData[i];就是这个意思;
2.取出下一个元素,在已经排序的元素序列中从后向前扫描;
3.如果该元素(已排序)大于新元素,将该元素移到下一位置;
4.重复步骤3,直到找到已排序的元素小于或者等于新元素的位置;
5.将新元素插入到该位置后;
6.重复步骤2~5.
两路归并排序(Merge Sort),也就是我们常说的归并排序,也叫合并排序。它是建立在归并操作上的一种有
效的排序算法。归并操作即将两个已经排序的序列合在一个序列的操作。该算法是采用分治算法的一个非常
典型的应用。
设归并排序的当前区间是R[low..high], 分治法的三个步骤是:
1.分解:将当前区间一分为二, 即求分裂点;
2.求解: 递归地对两个子区间R[low..mid]和R[mid+1..high]进行归并排序;
3.组合: 将已排序的两个子区间R[low..mid]和R[mid+1..high]归并为一个有序的区间R[low..high].
递归的终结条件: 子区间长度为1
快速排序的原理基于分治法,在进行排序时具体分为下面3个步骤。现假设待排序的序列为L[m..n]:
(1)首先要对待排序的序列进行分解,序列L[m..n]被划分成两种(可能为空的)子序列L[m..pivot-1]和
L[pivot+1 .. n],使L[m .. pivot-1]的每个元素均小于或等于L[pivot], 同时L[pivot+1 .. n]的
每个元素均大于L[pivot]。其中L[pivot]称为这一轮分割中的枢轴。
(2)然后再通过递归调用快速排序,对子序列L[m..pivot-1]和L[pivot+1 .. r]排序。
(3)最后两种子序列是已经排序的,所以对他们的合并时,整个序列L[m..n]已经排好序。
图(有黄色填充的部分代表主元)所示:
其中i和j分别是程序当中的两个下标,j的作用是循环遍历,i的作用是指向小于主元的最后的一个数。当循环结束之后就
冒泡排序的基本原理: 依次比较相邻的两个数,将大数放后面(右边),小数放在前面(左边)。
即首先比较第1个和第2个数,将大数放后(右),小数放前(左)。然后比较第2个数和第3个数,将大数放后(右),
小数放前(左),如此继续,直至比较最后两个数。此时第1轮结束,如此在第1轮之后位于最前的数必是所有数
最小的。然后使用上1轮的结果重复以上过程,将所有的最小数放到最前。到所有的数都已经有序时算法结束。
#include <iostream>
using namespace std;
void Swap(int array[], int i, int j)
{
int temp = array[i];
array[i] = array[j];
array[j] = temp;
}
void BubbleSort(int* pData, int Count)
{
for(int i = 1; i < Count; i++)
{
cout << "i = " << i << endl;
for(int j = Count-1; j >= i; j--)
{
cout << " j = " << j << endl;
if(pData[j] < pData[j-1])
Swap(pData, j, j-1);
}
}
}
int main()
{
int data[] = {10, 9, 8, 7, 6, 5, 4};
BubbleSort(data, 7);
for(int i =0; i < 7; i++)
cout << data[i] << " ";
cout << "\n";
return 0;
}
鸡尾酒排序(改进的冒泡排序)
还有一种改进的冒泡排序,原理是对要排序的数组进行双向冒泡排序,双向冒泡排序又称为鸡尾酒排序。
#include <iostream>
using namespace std;
void Swap(int array[], int i, int j)
{
int temp = array[i];
array[i] = array[j];
array[j] = temp;
}
void CocktailSort(int *arr, int size) //鸡尾酒排序
{
int tail = size-1;
for(int i = 0; i < tail;)
{
for(int j = tail; j > i; --j) //第一轮,先将最小的数派到前面
{
if(arr[j] < arr[j-1])
Swap(arr, j, j-1);
}
++i; //原来i处数据已排好序,加1
for(int j = i; j < tail; ++j) //第二轮,将最大数据派到后面
{
if(arr[j] > arr[j+1])
Swap(arr, j, j+1);
}
tail--; //原tail处数据也已排好序,将其减1
}
}
void OutPrint(int *arr, int Length)
{
for(int i = 0; i < Length; i++)
cout << arr[i] << " ";
cout << endl;
}
int main()
{
int arr[] = {1,4,5,56,5,6,14,98,9,5,233,32,6,48,8,98,9,5,6,35,2,3};
CocktailSort(arr,sizeof(arr)/sizeof(int));
OutPrint(arr, sizeof(arr)/sizeof(int));
return 0;
}
选择排序
选择排序从需要排序的队列数据中选择最小的同第一个值交换,再从剩下的部分中选择最小的与第二个交换,
这样往复下去,最后实现全队列的排序。
void SelectSort(int *pData, int Count)
{
int iTemp; //一个存储指
int iPos; //一个存储下标
for(int i = 0; i < Count-1; i++)
{
iTemp = pData[i];
iPos = i;
for(int j = i+1; j < Count; j++)
{
if(pData[j] < iTemp) //选择排序法就是用第一个元素与最小的元素交换
{
iTemp = pData[j];
iPos = j; //下标的交换赋值
}
}
pData[iPos] = pData[i];
pData[i] = iTemp;
}
}
直接插入排序
其基本思想是:把待排序的纪录按其关键码值的大小逐个插入到一个已经排好序的有序序列中,直到所有的
纪录插入完为止,得到一个新的有序序列。
直接插入算法把要排序的数组分成两部分
步骤大概是这样的:
1.从第一个元素开始,该元素可以认为已经被排序 代码中 iTemp = pData[i];就是这个意思;
2.取出下一个元素,在已经排序的元素序列中从后向前扫描;
3.如果该元素(已排序)大于新元素,将该元素移到下一位置;
4.重复步骤3,直到找到已排序的元素小于或者等于新元素的位置;
5.将新元素插入到该位置后;
6.重复步骤2~5.
void InsertSort(int* pData, int Count)
{
int iTemp;
int iPos;
for(int i = 1; i < Count; i++)
{
iTemp = pData[i];
iPos = i-1;
while((iPos >= 0) && (iTemp < pData[iPos]))
{
pData[iPos+1] = pData[iPos];
iPos--;
}
pData[iPos+1] = iTemp;
}
}
归并排序
两路归并排序(Merge Sort),也就是我们常说的归并排序,也叫合并排序。它是建立在归并操作上的一种有
效的排序算法。归并操作即将两个已经排序的序列合在一个序列的操作。该算法是采用分治算法的一个非常
典型的应用。
设归并排序的当前区间是R[low..high], 分治法的三个步骤是:
1.分解:将当前区间一分为二, 即求分裂点;
2.求解: 递归地对两个子区间R[low..mid]和R[mid+1..high]进行归并排序;
3.组合: 将已排序的两个子区间R[low..mid]和R[mid+1..high]归并为一个有序的区间R[low..high].
递归的终结条件: 子区间长度为1
void Merge(int array[], int p, int q, int r)
{
int i, k;
int begin1, end1, begin2, end2;
//分配空间以容纳临时结果,该空间是两个序列大小之和
int *temp = (int*)malloc((r-p+1)*sizeof(int));
begin1 = p; end1 = q;
begin2 = q+1; end2 = r;
k = 0;
while((begin1 <= end1) && (begin2 <= end2))
{
if(array[begin1] < array[begin2])
{
temp[k] = array[begin1];
begin1++;
}
else
{
temp[k] = array[begin2];
begin2++;
}
k++;
}
//如果左序列有些元素比有序列的所有元素都大时,经过上一步,左序例将剩下这些元素
//可以将这些元素直接附加到temp的末尾
while(begin1 <= end1)
temp[k++] = array[begin1++];
//如果右序列有些元素比左序列的所有元素都大时,经过上一步,右序列将剩下这些元素
//可以将这些元素直接附加到temp的末尾
while(begin2 <= end2)
temp[k++] = array[begin2++];
//将临时变量存储的结果保存在中
for(i=0; i <(r-p+1); i++)
{
array[p+i] = temp[i];
}
free(temp);
}
void Merge_sort(int array[], unsigned int first, unsigned int last)
{
int mid = 0;
if(first < last)
{
mid = (first + last)/2;
Merge_sort(array, first, mid);
Merge_sort(array, mid+1, last);
Merge(array, first, mid, last);
}
}
快速排序
快速排序的原理基于分治法,在进行排序时具体分为下面3个步骤。现假设待排序的序列为L[m..n]:
(1)首先要对待排序的序列进行分解,序列L[m..n]被划分成两种(可能为空的)子序列L[m..pivot-1]和
L[pivot+1 .. n],使L[m .. pivot-1]的每个元素均小于或等于L[pivot], 同时L[pivot+1 .. n]的
每个元素均大于L[pivot]。其中L[pivot]称为这一轮分割中的枢轴。
(2)然后再通过递归调用快速排序,对子序列L[m..pivot-1]和L[pivot+1 .. r]排序。
(3)最后两种子序列是已经排序的,所以对他们的合并时,整个序列L[m..n]已经排好序。
int Partition(int v[], int left, int right)
{
int x = v[right]; //将输入数组的最后一个数作为主元,用它来对数组进行划分
int i = left -1; //i是最后一个小于主元的数的下标
for(int j = left; j < right; j++) //遍历下标left到right-1的数
{
if(v[j] < x) //如果数小于主元的话就将i向前挪动一个位置,并且交换j和i所分别所指向的数
{
Swap(v, ++i, j);
}
}
//经历上面的循环之后下标为从left 到 i(包括i)的数就均小于x的数了,现在将主元和i+1 位置上面的数互换
v[right] = v[i+1];
v[i+1] = x;
return i+1;
}
void Quicksort(int v[], int left, int right) //执行快速排序
{
if(left < right)
{
int q = Partition(v, left, right);
Quicksort(v, left, q-1); //对左序列进行快速排序
Quicksort(v, q+1, right); //对右序列进行快速排序
}
}
partition函数的运行过程使用一个例子来帮助理解。对数组[6, 10, 10, 3, 7 ,1,8]运行一次Partition函数的过程如下
图(有黄色填充的部分代表主元)所示:
其中i和j分别是程序当中的两个下标,j的作用是循环遍历,i的作用是指向小于主元的最后的一个数。当循环结束之后就
将主元和i+1位置上面的数进行交换,这样就可以实现依据主元的大小对数组进行划分。
希尔排序
希尔排序的基本思想:先取一个小于n的整数d1作为第一个增量,把文件的全部记录分成d1个组。所有距离为d1的倍数的记录
放在同一个组中。先在各组内进行直接插入排序;然后,取第二个增量d2<d1重复上述的分组和排序,直至所取的增量dt = 1
(dt < dt-1 < ... < d2 < d1),即所有记录放在同一组中进行直接插入排序为止。
void ShellPass(int *array, int iSize, int d) //一轮增量为d的希尔插入排序
{
int temp;
for(int i = d; i < iSize; i++)
{
if(array[i] < array[i-d])
{
temp = array[i];
int j = i-d;
do{
array[j+d] = array[j];
j = j-d;
}while(j>0 && temp < array[j]);
array[j+d] = temp;
}
}
}
void Shellsort(int *array, int iSize) //希尔排序
{
int d = iSize; //一般增量设置为数组元素个数,不断除以2 以取小
do{
d = d/2;
ShellPass(array, iSize, d);
}while(d>1);
}