六大算法
一、冒泡排序(BubbleSort)
基本思想
两个数比较大小,较大的数下沉,较小的数冒起来。
过程
- 比较相邻的两个数据,如果第二个数小,就交换位置。
- 从后向前两两比较,一直到比较最前两个数据。最终最小数被交换到起始的位置,这样第一个最小数的位置就排好了。
- 继续重复上述过程,依次将第2.3…n-1个最小数排好位置。
平均时间复杂度
O(n2)
C代码实现
int arr[] = {34, 27, 55, 8, 97};
int length = sizeof(arr)/sizeof(arr[0]);
// 以小到大
void bubbleSort(int arr[], int length)
{
int temp;
for (int i = 0; i < length - 1; i ++) {
for (int j = length - 1; j > i; j --) {
if (arr[i] > arr[j]) {
temp = arr[j];
arr[j] = arr[i];
arr[i] = temp;
}
}
}
}
优化
-
针对问题
数据的顺序排好之后,冒泡算法仍然会继续进行下一轮的比较,直到arr.length-1次,后面的比较没有意义的。 -
方案
设置标志位flag,如果发生了交换flag设置为true;如果没有交换就设置为false。
这样当一轮比较结束后如果flag仍为false,即:这一轮没有发生交换,说明数据的顺序已经排好,没有必要继续进行下去。
void bubbleSort(int arr[], int length)
{
int temp;
Boolean flag; //是否交换的标志
for (int i = 0; i < length - 1; i ++) {
flag = false; // 默认未false
for (int j = length - 1; j > i; j --) {
if (arr[i] > arr[j]) {
temp = arr[j];
arr[j] = arr[i];
arr[i] = temp;
flag = true; //只要有发生了交换,flag就置为true
}
}
// 判断标志位是否为false,如果为false,说明后面的元素已经有序,就直接return
if (flag == false)
break;
}
}
二、二分查找(Binary Search)
二分查找也称折半查找(Binary Search)
,它是一种效率较高的查找方法。但是,折半查找要求线性表必须采用顺序存储结构
,而且表中元素按关键字有序
排列。
算法要求
- 必须采用顺序存储结构。
- 必须按关键字大小有序排列。
时间复杂度
O(log2n)
查找过程
首先,假设表中元素是按升序排列,
将表中间位置记录的关键字与查找关键字比较,如果两者相等,则查找成功;
否则利用中间位置记录将表分成前、后两个子表,如果中间位置记录的关键字大于查找关键字,则进一步查找前一子表,否则进一步查找后一子表。
重复以上过程,直到找到满足条件的记录,使查找成功,或直到子表不存在为止,此时查找不成功。
C代码示例
int arr1[] = {1, 2, 7, 8, 9};
int target = BinarySearch(arr1, 0, length - 1, 8);
/// low=0, high=n-1, target = 目标
int BinarySearch(int arr[], int low, int high, int target)
{
/* 在有序表R[0..n-1]中进行二分查找,成功时返回结点的位置,失败时返回-1 */
while(low <= high)
{
int mid = (low + high)/2; //还是溢出问题
if(arr[mid] > target)
high = mid - 1;
else if(arr[mid] < target)
low = mid + 1;
else
return mid;
}
return -1;
}
三、快速排序(Quicksort)
基本思想
- 先从数列中取出一个数作为key值;
- 将比这个数小的数全部放在它的左边,大于或等于它的数全部放在它的右边;
- 对左右两个小数列重复第二步,直至各区间只有1个数。
示例
假设用户输入了如下数组:
下标 | 0 | 1 | 2 | 3 | 4 | 5 |
---|---|---|---|---|---|---|
数据 | 6 | 2 | 7 | 3 | 8 | 9 |
创建变量i=0(指向第一个数据)
, j=5(指向最后一个数据)
, k=6(赋值为第一个数据的值)
。
我们要把所有比k小的数移动到k的左面,所以我们可以开始寻找比6小的数,从j开始,从右往左找,不断递减变量j的值,我们找到第一个下标3的数据比6小,于是把数据3移到下标0的位置,把下标0的数据6移到下标3,完成第一次比较:
下标 | 0 | 1 | 2 | 3 | 4 | 5 |
---|---|---|---|---|---|---|
数据 | 3 | 2 | 7 | 6 | 8 | 9 |
i=0 j=3 k=6
接着,开始第二次比较,这次要变成找比k大的了,而且要从前往后找了。递加变量i,发现下标2的数据是第一个比k大的,于是用下标2的数据7和j指向的下标3的数据的6做交换,数据状态变成下表:
下标 | 0 | 1 | 2 | 3 | 4 | 5 |
---|---|---|---|---|---|---|
数据 | 3 | 2 | 6 | 7 | 8 | 9 |
i=2 j=3 k=6
称上面两次比较为一个循环。
接着,再递减变量j,不断重复进行上面的循环比较。
在本例中,我们进行一次循环,就发现i和j“碰头”了:他们都指向了下标2。于是,第一遍比较结束。得到结果如下,凡是k(=6)左边的数都比它小,凡是k右边的数都比它大:
下标 | 0 | 1 | 2 | 3 | 4 | 5 |
---|---|---|---|---|---|---|
数据 | 3 | 2 | 6 | 7 | 8 | 9 |
如果i和j没有碰头的话,就递加i找大的,还没有,就再递减j找小的,如此反复,不断循环。注意判断和寻找是同时进行的。
然后,对k两边的数据,再分组分别进行上述的过程,直到不能再分组为止。
注意:
第一遍快速排序不会直接得到最终结果,只会把比k大和比k小的数分到k的两边。为了得到最后结果,需要再次对下标2两边的数组分别执行此步骤,然后再分解数组,直到数组不能再分解为止(只有一个数据),才能得到正确结果。
平均时间复杂度
O(N*logN)
C代码实现
int arr[] = {34, 27, 55, 8, 97};
int length = sizeof(arr)/sizeof(*arr);
quickSort(arr, 0, length-1);
/// 以小到大
void quickSort(int *a, int left, int right)
{
if(left >= right)/*如果左边索引大于或者等于右边的索引就代表已经整理完成一个组了*/
{
return ;
}
int i = left;
int j = right;
int key = a[left];
while(i < j) { /*控制在当组内寻找一遍*/
/*而寻找结束的条件就是,1,找到一个小于或者大于key的数(大于或小于取决于你想升
序还是降序)2,没有符合条件1的,并且i与j的大小没有反转*/
while(i < j && key <= a[j]) {
j--;/*向前寻找*/
}
a[i] = a[j];
/*找到一个这样的数后就把它赋给前面的被拿走的i的值(如果第一次循环且key是
a[left],那么就是给key)*/
/*这是i在当组内向前寻找,同上,不过注意与key的大小关系停止循环和上面相反,
因为排序思想是把数往两边扔,所以左右两边的数大小与key的关系相反*/
while(i < j && key >= a[i]) {
i++;
}
a[j] = a[i];
}
a[i] = key;/*当在当组内找完一遍以后就把中间数key回归*/
quickSort(a, left, i - 1);/*最后用同样的方式对分出来的左边的小组进行同上的做法*/
quickSort(a, i + 1, right);/*用同样的方式对分出来的右边的小组进行同上的做法*/
/*当然最后可能会出现很多分左右,直到每一组的i = j 为止*/
}
四、希尔排序(Shell Sort)
基本思想
在要排序的一组数中,根据某一增量分为若干子序列,并对子序列分别进行插入排序。
然后逐渐将增量减小,并重复上述过程。直至增量为1,此时数据序列基本有序,最后进行插入排序。
过程
平均时间复杂度
O(n1.5)
C代码实现
int arr[] = {34, 27, 55, 8, 97};
int length = sizeof(arr)/sizeof(arr[0]);
void shellSort(int arr[], int length)
/
// 以小到大
void shellSort(int arr[], int length)
{
int temp = 0;
int incre = length;
while(true){
incre = incre/2;
for(int k = 0;k<incre;k++){ //根据增量分为若干子序列
for(int i=k+incre;i<length;i+=incre){
for(int j=i;j>k;j-=incre){
if(arr[j]<arr[j-incre]){
temp = arr[j-incre];
arr[j-incre] = arr[j];
arr[j] = temp;
}else{
break;
}
}
}
}
if(incre == 1){
break;
}
}
}
五、选择排序(SelctionSort)
基本思想
在长度为N的无序数组中,第一次遍历n-1个数,找到最小的数值与第一个元素交换;
第二次遍历n-2个数,找到最小的数值与第二个元素交换;
。。。
第n-1次遍历,找到最小的数值与第n-1个元素交换,排序完成。
过程
平均时间复杂度
O(n2)
C代码实现
int arr[] = {34, 27, 55, 8, 97};
int length = sizeof(arr)/sizeof(*arr);
selectSort(arr, length);
//
/// 以小到大排序
void selectSort(int arr[], int length)
{
for (int i = 0; i < length - 1; i ++) {
int minIndex = i;
for (int j = i + 1; j < length; j ++) {
if (arr[minIndex] > arr[j]) {
minIndex = j;
}
}
/// 找到最小值,进行替换
if (minIndex != i) {
int temp = arr[i];
arr[i] = arr[minIndex];
arr[minIndex] = temp;
}
}
}
六、插入排序(Insertion Sort)
基本思想
在要排序的一组数中,假定前n-1个数已经排好序,现在将第n个数插到前面的有序数列中,使得这n个数也是排好顺序的。如此反复循环,直到全部排好顺序。
过程
平均时间复杂度
O(n2)
C代码实现
int arr[] = {34, 27, 55, 8, 97};
int length = sizeof(arr)/sizeof(*arr);
insertSort(arr, length);
/// 以小到大
void insertSort(int arr[], int length)
{
for (int i = 0; i < length - 1; i ++) {
for (int j = i + 1; j > 0; j --) {
if (arr[j] < arr[j - 1]) {
int temp = arr[j - 1];
arr[j - 1] = arr[j];
arr[j] = temp;
} else { // 当第j-1个数据小于第j个数据时,后面的也就都小于,没必要再遍历
break;
}
}
}
}