插入排序:简单地说,就是就将无序序列依次插入到有序序列中。
算法描述:
1. 从第一个元素开始,该元素可以认为已经被排序
2. 取出下一个元素,在已经排序的元素序列中从后向前扫描
3. 如果该元素(已排序)大于新元素,将该元素移到下一位置
4. 重复步骤3,直到找到已排序的元素小于或者等于新元素的位置
5. 将新元素插入到该位置后
6. 重复步骤2~5
时间复杂度:
最坏情况;O(n^2);
平 均:O(n^2);
冒泡排序:一种简单地排序算法。
算法描述:
1. 比较相邻的元素。如果第一个比第二个大,就交换他们两个。
2. 对每一对相邻元素作同样的工作,从开始第一对到结尾的最后一对。这样会使得最后的元素为最大值;
3. 持续每次对越来越少的元素重复上面的步骤,直到没有任何一对数字需要比较。
时间复杂度:
最坏情况;O(n^2);
平 均:O(n^2);
选择排序:从未排序列中找到最大(小)元素将其置于合适的位置。
算法描述:
1. 在未排序序列中找到最小(大)元素,存放到排序序列的起始位置,
2. 从剩余未排序元素中继续寻找最小(大)元素,放到已排序序列的末尾。
3. 重复步骤2,直到所有元素均排序完毕。
时间复杂度:
最坏情况;O(n^2);
平 均:O(n^2);
快速排序:通过一趟排序将待排记录分隔成独立的两部分,其中以部分记录的关键字比另一部分记录的关键字小,
则可分别对这两部分记录继续进行排序,以达到整个序列有序。
算法描述:
1. 设置两个变量low、high,排序开始的时候:low=0,high=N-1;
2. 以第一个数组元素作为关键数据,赋值给key,即key=A[0];
3. 从high开始向前搜索,即由后开始向前搜索(high--),找到第一个小于key的值p[high],将值为key的项与p[high]交换;
4. 从low开始向后搜索,即由前开始向后搜索(low++),找到第一个大于key的p[low],将值为key的项与p[low]交换;
5. 重复第3、4、5步,直到low=high; (3,4步中,没找到符合条件的值,即3中p[high]不小于key,4中p[low]不大于key的时候改变low、high的值,使得high=high-1,low=low+1,直至找到为止。找到符合条件的值,进行交换的时候low,high指针位置不变。另外,low==high时令循环结束)。
时间复杂度:
最坏情况:O(n^2);
平 均:O(nlogn);
堆排序:是指利用堆这种数据结构所设计的一种排序算法。
算法思想:
1. 初始化操作:将R[1..n]构造为“大顶堆”;
2. 将当前无序区的堆顶记录R[0]和该序列的最后一个记录交换,然后将新的无序区调整为堆(亦称重建堆);
3. 直到所有元素有序;
时间复杂度:
最坏情况;O(nlogn);
平 均:O(nlogn);
各种内部排序方法的比较:
1. 就平均时间性能而言,快速排序最佳,其所需时间最少,但快速排序在最坏情况下的性能不如堆排序;
2. 由于堆排序在最坏情况下的时间复杂度和平均时间复杂度差不多,所以待排序列中元素初始时的顺序对该排序算法没有太大影响。
2. 从方法的稳定性来看,所有时间复杂度为O(n^2)的简单排序法都是稳定的(包括插入排序、冒泡排序、选择排序)。一般来说,排序过程中的“比较”是在“相邻的两个记录关键字”间进行的排序方法都是稳定的。
算法描述:
1. 从第一个元素开始,该元素可以认为已经被排序
2. 取出下一个元素,在已经排序的元素序列中从后向前扫描
3. 如果该元素(已排序)大于新元素,将该元素移到下一位置
4. 重复步骤3,直到找到已排序的元素小于或者等于新元素的位置
5. 将新元素插入到该位置后
6. 重复步骤2~5
时间复杂度:
最坏情况;O(n^2);
平 均:O(n^2);
/*********** 插入排序 ************/
void InsertSort(int *p, const int len)
{
assert(p != NULL);
int i;
int j;
int temp;
for (i = 1; i < len; i++)
{
temp = p[i];
for (j = i-1; j >= 0; j--)
{
if (p[i] > temp)
{
p[j+1] = p[j];
}
else
{
break;
}
}
p[j+1] = temp;
}
}
冒泡排序:一种简单地排序算法。
算法描述:
1. 比较相邻的元素。如果第一个比第二个大,就交换他们两个。
2. 对每一对相邻元素作同样的工作,从开始第一对到结尾的最后一对。这样会使得最后的元素为最大值;
3. 持续每次对越来越少的元素重复上面的步骤,直到没有任何一对数字需要比较。
时间复杂度:
最坏情况;O(n^2);
平 均:O(n^2);
/*********** 冒泡排序 ************/
void BubbleSort(int *p, const int len)
{
assert(p != NULL);
int i;
int j;
int temp;
for (i = 0; i < len; i++)
{
for (j = 0; j < len-i-1; j++)
{
if (p[j] > p[j+1])
{
temp = p[j];
p[j] = p[j+1];
p[j+1] = temp;
}
}
}
}
/*********** 冒泡排序 ************/
void BubbleSort(int *p, const int len)
{
assert(p != NULL);
int i;
int j;
int temp;
for (i = 0; i < len; i++)
{
for (j = i+1; j < len; j++)
{
if (p[i] > p[j])
{
temp = p[i];
p[i] = p[j];
p[j] = temp;
}
}
}
}
选择排序:从未排序列中找到最大(小)元素将其置于合适的位置。
算法描述:
1. 在未排序序列中找到最小(大)元素,存放到排序序列的起始位置,
2. 从剩余未排序元素中继续寻找最小(大)元素,放到已排序序列的末尾。
3. 重复步骤2,直到所有元素均排序完毕。
时间复杂度:
最坏情况;O(n^2);
平 均:O(n^2);
/*********** 选择排序 ************/
void SelectSort(int *p, const int len)
{
assert(p != NULL);
int i;
int j;
int k;
int temp;
for (i = 0; i < len-1; i++)
{
k = i;
for (j = i+1; j < len; j++)
{
if (p[k] > p[j])
{
k = j;
}
}
if (k != i)
{
temp = p[i];
p[i] = p[k];
p[k] = temp;
}
}
}
快速排序:通过一趟排序将待排记录分隔成独立的两部分,其中以部分记录的关键字比另一部分记录的关键字小,
则可分别对这两部分记录继续进行排序,以达到整个序列有序。
算法描述:
1. 设置两个变量low、high,排序开始的时候:low=0,high=N-1;
2. 以第一个数组元素作为关键数据,赋值给key,即key=A[0];
3. 从high开始向前搜索,即由后开始向前搜索(high--),找到第一个小于key的值p[high],将值为key的项与p[high]交换;
4. 从low开始向后搜索,即由前开始向后搜索(low++),找到第一个大于key的p[low],将值为key的项与p[low]交换;
5. 重复第3、4、5步,直到low=high; (3,4步中,没找到符合条件的值,即3中p[high]不小于key,4中p[low]不大于key的时候改变low、high的值,使得high=high-1,low=low+1,直至找到为止。找到符合条件的值,进行交换的时候low,high指针位置不变。另外,low==high时令循环结束)。
时间复杂度:
最坏情况:O(n^2);
平 均:O(nlogn);
/*********** 调整分区 ************/
int Partition(int *p, const int low, const int high)
{
assert(p != NULL);
int i = low;
int j = high;
int key = p[low];
while (i < j)
{
while (i < j && key <= p[j])
{
j--;
}
if (i < j)
{
p[i++] = p[j];
}
while (i < j && key > p[i])
{
i++;
}
if (i < j)
{
p[j--] = p[i];
}
}
p[i] = key;
return i;
}
/*********** 快速排序 ************/
void QuickSort(int *p, const int low, const int high)
{
assert(p != NULL);
int pos = Partition(p, low, high);
if (low < high)
{
QuickSort(p, low, pos-1); // 递归地对关键字前面的序列进行排序
QuickSort(p, pos+1, high); // 递归地对关键字后面的序列进行排序
}
}
堆排序:是指利用堆这种数据结构所设计的一种排序算法。
算法思想:
1. 初始化操作:将R[1..n]构造为“大顶堆”;
2. 将当前无序区的堆顶记录R[0]和该序列的最后一个记录交换,然后将新的无序区调整为堆(亦称重建堆);
3. 直到所有元素有序;
时间复杂度:
最坏情况;O(nlogn);
平 均:O(nlogn);
/*********** 构建大顶堆 ************/
void AdjustTop(int *p, const int len)
{
assert(p != NULL);
int pos = len / 2 - 1;
int t;
int temp;
while (pos >= 0)
{
if (pos*2+2 == len) // 只有自由左子树的情况
{
t = p[pos*2+1] > p[pos] ? pos*2+1 : pos;
}
else // 左右子树都存在
{
t = p[pos*2+1] > p[pos*2+2] ? pos*2+1 : pos*2+2;
t = p[t] > p[pos] ? t : pos;
}
if (t != pos) // 加此条件判断以减少交换次数
{
temp = p[pos];
p[pos] = p[t];
p[t] = temp;
}
pos--;
}
}
/*********** 堆排序 ************/
void HeapSort(int *p, const int len)
{
assert(p != NULL);
int i;
int temp;
for (i = 0; i < len; i++)
{
AdjustTop(p, len-i); //对剩余元素重新构建“大顶堆”
temp = p[0];
p[0] = p[len-i-1];
p[len-i-1] = temp;
}
}
各种内部排序方法的比较:
1. 就平均时间性能而言,快速排序最佳,其所需时间最少,但快速排序在最坏情况下的性能不如堆排序;
2. 由于堆排序在最坏情况下的时间复杂度和平均时间复杂度差不多,所以待排序列中元素初始时的顺序对该排序算法没有太大影响。
2. 从方法的稳定性来看,所有时间复杂度为O(n^2)的简单排序法都是稳定的(包括插入排序、冒泡排序、选择排序)。一般来说,排序过程中的“比较”是在“相邻的两个记录关键字”间进行的排序方法都是稳定的。