排序方法的稳定性:
对于关键字相同的元素,在其排序完后领先关系保持不变,即原来是…x1…x2…(x1和x2相同),在排序完后依然是…x1…x2…的位置,说明该排序是稳定的,反之是不稳定的。
1.插入排序
算法思想:
实现将一组数从小到大排列,即从数的第二个开始往前比较,如果它比前面相邻的数小,将它拎出来,然后前面的数往前进一个位置,至于将这个拎出来的数放在哪个位置,当然是放在比前面的数大的前一个位置。(前面的数是已经排完有序的)
代码实现:
int s
[100];
int n
;
for(int i
=2;i
<=n
;i
++)
{
if(s
[i
]>=s
[i
-1]) continue;
s
[0]=s
[i
];
int j
=i
-1;
for(;s
[0]<s
[j
];j
--)
{
s
[j
+1]=s
[j
];
}
s
[j
+1]=s
[0];
}
算法分析:
时间复杂度:O(n^2) 空间复杂度:O(1) 算法稳定性:稳定
2.冒泡排序
算法思想:
实现将n个数从小到大排序,两两比较,将最大的数放右边,一趟比较完后最右边的数即为最大数,然后n-1个数继续两两比较,将最大的数放在最右边,这是第二趟,共需要进行n-1趟。
代码实现:
int s
[100];
int n
;
for(int i
=0;i
<n
-1;i
++)
{
bool flag
=true;
for(int j
=0;j
<n
-i
-1;j
++)
{
if(s
[j
]>s
[j
+1])
{
swap(s
[j
],s
[j
+1]);
flag
=false;
}
}
if(flag
==true) break;
}
算法分析:
时间复杂度:O(n^2) 空间复杂度:O(1) 算法稳定性:稳定
3.选择排序
算法思想:
将一组数从小到大排序,类似冒泡排序,只不过不是一个个交换,而是在一组数中找出最小的数位置,与第一个交换数值,然后从后面找出最小的数位置与第二个交换数值,依次内推。
代码实现:
int s
[100];
int n
;
for(int i
=0;i
<n
-1;i
++)
{
int k
=i
;
for(int j
=i
+1;j
<n
;j
++)
{
if(s
[j
]<s
[k
])
{
k
=j
;
}
}
swap(s
[i
],s
[k
]);
}
算法分析:
时间复杂度:O(n^2) 空间复杂度:O(1) 算法稳定性:不稳定
4.快速排序
算法思想:
将一组数从小到大排序,将首元素设置为哨兵,将后面的每个数与哨兵比较,比它小,放左边,比它大放右边,再把哨兵放在中间的位置,其左边的数都比它小,其右边的数都比它大,这样就实现了一趟快速排序,再把哨兵的左边和右边看成一个新的数组进行快速排序,依次进行,直到只剩一个元素时停止。
代码实现:
int s
[1000];
int n
;
int quicksort(int low
,int high
)
{
int x
=s
[low
];
while(low
<high
)
{
while(low
<high
&&s
[high
]>=x
) high
--;
s
[low
]=s
[high
];
while(low
<high
&&s
[low
]<=x
) low
++;
s
[high
]=s
[low
];
}
s
[low
]=x
;
return low
;
}
void mysort(int low
,int high
)
{
if(high
<=low
) return;
int k
=quicksort(low
,high
);
mysort(low
,k
-1);
mysort(k
+1,high
);
}
mysort(0,n
-1)
算法分析:
时间复杂度:O(nlogn) 空间复杂度:O(logn) (递归造成的栈空间) 算法稳定性:不稳定
5.堆排序
算法思想:
这个稍微复杂一些,首先需要了解什么是堆,堆是一棵完全二叉树,其每个节点都大于或者小于它的左孩子和右孩子,当每个节点的值都大于它的左孩子和右孩子时,这叫大顶堆,当每个节点的值都小于它的左孩子和右孩子时,这叫小顶堆。 实现一组数从小到大排序,首先需要建大顶堆,可以把这个数组看成一个完全二叉树,第 i 个数看成一个节点,那么它的左孩子为 2*i ,右孩子为 2*i+1。当建完后,不管顺序如何,大顶堆的第一个元素一定是最大值,所以将它与最后一个数交换位置,再重新建堆。类似冒泡排序,不断将最大值放在最后面。
代码实现:
int s
[1000];
int n
;
void HeapAdjust(int x
,int m
)
{
int rc
=s
[x
];
for(int i
=2*x
;i
<=m
;i
=i
*2)
{
if(i
+1<=m
&&s
[i
]<s
[i
+1]) i
++;
if(rc
>=s
[i
]) break;
s
[x
]=s
[i
];
x
=i
;
}
s
[x
]=rc
;
}
void HeapSort(int n
)
{
for(int i
=n
/2;i
>0;i
--)
{
HeapAdjust(i
,n
);
}
for(int i
=n
;i
>1;i
--)
{
swap(s
[1],s
[i
]);
HeapAdjust(1,i
-1);
}
}
算法分析:
时间复杂度:O(nlogn) 空间复杂度:O(1) 算法稳定性:不稳定
6.归并排序
算法思想:
实现一组数从小到大排序,核心即拆合,先将这组数拆成两两一组,按从小到大排序好,即有序,再将各个数组两两合并。
核心代码:
int s
[1000];
int fu
[1000];
int n
;
void combine(int low
,int mid
,int high
)
{
int k
=low
,i
=low
,j
=mid
+1;
while(i
<=mid
&&j
<=high
)
{
if(s
[i
]<s
[j
])
{
fu
[k
++]=s
[i
++];
}
else
{
fu
[k
++]=s
[j
++];
}
}
while(i
<=mid
) fu
[k
++]=s
[i
++];
while(j
<=high
) fu
[k
++]=s
[j
++];
k
=low
;
while(k
<=high
)
{
s
[k
]=fu
[k
];k
++;
}
}
void CombineSort(int low
,int high
)
{
if(low
>=high
) return;
int mid
=(low
+high
)/2;
CombineSort(low
,mid
);
CombineSort(mid
+1,high
);
combine(low
,mid
,high
);
}
算法分析:
时间复杂度:O(nlogn) 空间复杂度:O(n) 算法稳定性:稳定