数据结构绝对是重中之重!
排序算法主要有9种
冒泡排序 交换排序 选择排序 插入排序 快速排序 归并排序 堆排序 基排序 希尔排序
每个排序都有自己的适宜场合,有不同的时间复杂度和空间复杂度。
时间复杂度主要是从比较次数和移动次数来看
接下来一一介绍
一、冒泡排序
算法思想:
1.待排序的数据看作气泡,通过n-1趟比较,从而使较小的数据浮在上层(前),较大的数据沉在下层(后)。
2.每一趟比较,从下层开始比较两个相邻的数据,如果上层的数据大于下层的数据,则交换;否则,不移动顺序继续往上比较。
void BubbleSort(int n,List &A){
for (int i=0;i=i+1;j--)
{
if (A[j].key
比较次数 | 移动次数 | |||
最好情况 | n-1 | 0 | ||
最坏情况 | n(n-1)/2 | 3n(n-1)/2 |
平均时间复杂度 | 最差时间复杂度 | 空间复杂度 |
O(N*N) | O(N*N) | O(1) |
算法思想:(分治算法)
1.找基准元素。FindPivot(int i,int j)
作用是,使数列左侧都比基准元素要小,右侧都比基准元素要大。
如何选取?从左侧开始查找两个不同数值中的较大值作为基准元素。如果找不到两个不相同的元素,则返回0。排序结束。例如 3 3 2 5 6 1 0 则选取3为基准元素。
2.划分
(1)l表示从左边开始遍历的下标,r表示从右边开始遍历的下标。l为指向数列中从左边开始第一个比基准元素大的值;r为数列中从右边开始第一个比基准元素小的值。
(2)如果l
如果l>r,也即l=r+1。数列从l-1和l之间划分成两部分。
3.对于划分后的两个数列,继续采用快排算法。
int FindPivot(int i,int j,List &A){//选取基准元素,从数列左侧查找两个不同数值中的较大值为基准元素
for (int k=i+1;k<=j;k++)
{
if (A[k].key>A[i].key)
return k;
else if (A[k].key=Pivot;r--);
if (l
不过这里有点小错误啦~
就是结束递归结束条件设置的是pivotindex!=0,但是确实第一次可能pivotindex=0,如果结束,就没有进行排序。
最好情况
每次划分后,划分点左侧和右侧长度相同,则时间复杂度为O(nlogn)
T(n)<=2T(n/2)+n
<=2(2T(n/4)+n/2)+n
<=4(2T(n/8)+n/4)+2n
......
<=nT(1)+nlogn=O(nlogn)
最坏情况
每次划分只得到一个比上一次划分少一个记录的子序列
平均时间复杂度 | 最差时间复杂度 | 空间复杂度 |
O(nlogn) | O(N*N) | O(logn) |
算法思想:
与冒泡排序类似,冒泡排序,是找到比前面元素小的元素就交换。而选择排序则是在序列中找到最小的元素,然后与最前面的元素互换
void SelectSort(int n,List &A)
{
int low;
int lowindex;
for (int i=0;i
性能分析
算法思想:
数列->完全二叉树->初始建堆->整理堆
1.将数列按照完全二叉树的下标排成完全二叉树。
2.对该完全二叉树用PushDown算法依次对n/2,n/2-1,n/2-2,...1建堆。
3.取堆顶元素一定是最小值。
4.交换第N个和第1个元素。重复3。知道取遍所有元素。
首先必须明确一点,二叉树有完整的排序规则,从1开始到n.若某个节点为i,左孩子为2*i,右孩子为2*i+1.
void PushDown(int first,int last,List &A){//整理堆算法
int r=first;
while (r<=last/2)//如果r<=last/2,则为堆。否则都为叶子节点
{
if (r==last/2 && last%2==0)//r节点只有左节点
{
if (A[r].key>A[last].key)
{
swap(A[r],A[last]);
}
r=last;
}
else if (A[r].key>A[2*r].key && A[2*r].key<=A[2*r+1].key)//r节点有左右孩子,左孩子小于根节点,左孩子小于右节点,则将根节点和左孩子交换
{
swap(A[r],A[2*r]);
r*=2;
}
else if (A[2*r+1].key=1;i--)
PushDown(i,n,A);
for (i=n;i>=2;i--){
swap(A[1],A[i]);
PushDown(1,i-1,A);
}
}
void InsertSort(int size,List &A)
{
A[0].key=-100000;//哨兵
for (int i=1;i<=size;i++)
{
int j;
j=i;//A[j]为待排序的值,在数列中i之前的为有序区
//将j插入正确的位置
while (A[j-1].key>A[j].key)//如果j之前的数值比A[j]大,则将该数值与A[j]交换
{
swap(A[j-1],A[j]);
j=j-1;
}
}
}
算法思想:
1.第一遍归并是一一归并。第二遍是二二归并,第三遍是四四归并。第n遍就是2的N次方。
2.归并是两个列表的归并,指针i,j分别指向A中的两段{p...q-1}{q...r}.比较A[i],A[j]的大小,将小者赋给B[k]。如果两段序列不一样长,则将剩下的直接赋给B[].【Merge(int p,int q,int r,List A,List B)】
3.每一遍归并,设当前归并长度为h,,从序列开头开始,尽量归并2个h的序列,直达归并不了,剩下的序列,如果大于h小于2h,则归并两段,如果小于h,则直接复制到归并后的序列。【MergePass(int n,int h,List A,List B)】
4.归并排序【MergeSort(int n,List &A)】,当前归并序列长度大于n,终止排序。若小于n,开始一趟排序。
/****************归并排序*********************/
void Merge(int p,int q,int r,List A,List B)//A中两个序列【p...q-1】【q...r】合并到B
{
int i,j,k;
i=p;
j=q+1;
k=p;
while (i<=q&&j<=r)
{
if (A[i].key <= A[j].key)
B[k++]=A[i++];
else
B[k++]=A[j++];
}
while (i<=q)
{
B[k++]=A[i++];
}
while (j<=r)
{
B[k++]=A[j++];
}
}
void MergePass(int n,int h,List A,List B){//把A中长度为h的相邻序列归并为长度为2h
int i;
for (i=1;i+2*h-1<=n;i+=2*h)//归并长度为h的两个子序列
{
Merge(i,i+h-1,i+2*h-1,A,B);
}
if (i+h-1
void MergeSort_q(int low,int high,List &A,List &B){//折半归并
int mid=(low+high)/2;//两序列分解,A[LOW...HIGH]一分为二,mid=LOW+HIGH
if (low
void main()
{
List B;
input_1();
MergeSort_q(1,size,L,B);
output_1(size,B);
}
算法思想:
1.