一 排序算法
1.1 冒泡法排序
-
冒泡排序(Bubble Sort)是一种简单直观的排序算法。它重复地走访过要排序的数列,一次比较两个元素,如果他们的顺序错误就把他们交换过来。
-
首先从数组的第一个元素开始到数组最后一个元素为止,对数组中相邻的两个元素进行比较,如果位于数组左端的元素大于数组右端的元素,则交换这两个元素在数组中的位置。这样操作后数组最右端的元素即为该数组中所有元素的最大值。接着对该数组除最右端的n-1个元素进行同样的操作,再接着对剩下的n-2个元素做同样的操作,直到整个数组有序排列。
1.2 时间复杂度
冒泡法的时间复杂度为O(n^2)。
1.3 稳定性
冒泡法排序是一种稳定的算法
算法稳定性:假设在数列中存在a[i]=a[j],若在排序之前,a[i]在a[j]前面;并且排序之后,a[i]仍然在a[j]前面。则这个排序算法是稳定的!
1.4 实现
#include <stdio.h>
void bubble_sort(int arr[], int len) {int i, j, temp;for (i = 0; i < len - 1; i++)for (j = 0; j < len - 1 - i; j++)if (arr[j] > arr[j + 1]) {temp = arr[j];arr[j] = arr[j + 1];arr[j + 1] = temp;}
}
int main() {int arr[] = { 22, 34, 3, 32, 82, 55, 89, 50, 37, 5, 64, 35, 9, 70 };int len = (int) sizeof(arr) / sizeof(*arr);bubble_sort(arr, len);int i;for (i = 0; i < len; i++)printf("%d ", arr[i]);return 0;
}
1.2 选择排序
1.2.1 基本逻辑
选择排序的基本思想描述为:
-
首先在未排序序列中找到最小(大)元素,存放到排序序列的起始位置。
-
再从剩余未排序元素中继续寻找最小(大)元素,然后放到已排序序列的末尾。
-
重复第二步,直到所有元素均排序完毕。
1.2.2 时间复杂度
选择排序的时间复杂度为O(n^2)。
1.2.3 稳定性
选择排序是一种不稳定的算法
1.2.4 实现
void swap(int *a,int *b)
{int temp = *a;*a = *b;*b = temp;
}
void selection_sort(int arr[], int len)
{int i,j;for (i = 0 ; i < len - 1 ; i++){int min = i;for (j = i + 1; j < len; j++) if (arr[j] < arr[min]) min = j; swap(&arr[min], &arr[i]); }
}
1.3 插入排序
1.3.1 基本逻辑
-
插入排序的基本思想就是将无序序列插入到有序序列中。
-
插入排序的的原理应该是最容易理解的了,因为只要打过扑克牌的人都应该能够秒懂。插入排序是一种最简单直观的排序算法,它的工作原理是通过构建有序序列,对于未排序数据,在已排序序列中从后向前扫描,找到相应位置并插入。
-
步骤:
-
将第一待排序序列第一个元素看做一个有序序列,把第二个元素到最后一个元素当成是未排序序列。
-
从头到尾依次扫描未排序序列,将扫描到的每个元素插入有序序列的适当位置。(如果待插入的元素与有序序列中的某个元素相等,则将待插入元素插入到相等元素的后面。)
-
-
图示:
-
第一轮: 从第二位置的 6 开始比较,比前面 7 小,交换位置。
-
1.3.2 时间复杂度
插入排序的时间复杂度为O(n^2)。
1.3.3 稳定性
插入排序是稳定的排序算法
1.3.4 实现
void insertion_sort(int arr[], int len){int i,j,key;for (i=1;i<len;i++){key = arr[i];j=i-1;while((j>=0) && (arr[j]>key)) {arr[j+1] = arr[j];j--;}arr[j+1] = key;}
}
1.4 快速排序
1.4.1 基本逻辑
-
快速排序的基本思想是:通过一趟排序将待排记录分割成独立的两部分,其中一部分记录的关键字均比另一部分记录的关键字小,则可分别对这两部分记录继续进行排序,已达到整个序列有序。
-
快速排序是一种分治法。
-
一趟快速排序的具体过程可描述为:
-
从数列中挑出一个元素,称为 "基准"(pivot);
-
重新排序数列,所有元素比基准值小的摆放在基准前面,所有元素比基准值大的摆在基准的后面(相同的数可以到任一边)。在这个分区退出之后,该基准就处于数列的中间位置。这个称为分区(partition)操作;
-
递归地(recursive)把小于基准值元素的子数列和大于基准值元素的子数列排序;
-
1.4.2 时间复杂度
平均时间复杂度为
1.4.3 稳定性
快排法是一种不稳定的排序算法。
1.4.4 实现
一趟快速排序的具体做法为:设置两个指针low和high分别指向待排序列的开始和结尾,记录下基准值baseval(待排序列的第一个记录),然后先从high所指的位置向前搜索直到找到一个小于baseval的记录并互相交换,接着从low所指向的位置向后搜索直到找到一个大于baseval的记录并互相交换,重复这两个步骤直到low=high为止。
// 快速排序
void QuickSort(int arr[], int start, int end)
{if (start >= end)return;int i = start;int j = end;// 基准数int baseval = arr[start];while (i < j){// 从右向左找比基准数小的数while (i < j && arr[j] >= baseval){j--;}if (i < j){arr[i] = arr[j];i++;}// 从左向右找比基准数大的数while (i < j && arr[i] < baseval){i++;}if (i < j){arr[j] = arr[i];j--;}}// 把基准数放到i的位置arr[i] = baseval;// 递归QuickSort(arr, start, i - 1);QuickSort(arr, i + 1, end);
}
1.5 归并排序
1.5.1 基本逻辑
-
归并排序是建立在归并操作上的一种有效的排序算法。该算法是采用分治法的一个非常典型的应用。
-
“归并”的含义是将两个或两个以上的有序序列组合成一个新的有序表。假设初始序列含有n个记录,则可以看成是n个有序的子序列,每个子序列的长度为1,然后两两归并,多个有序子序列,再两两归并。如此重复,直到得到一个长度为n的有序序列为止。这种排序方法称为2-路归并排序。
1.5.2 时间复杂度
归并排序的时间复杂度是O(n㏒n)。
但是,归并排序的空间复杂度是O(n)。
1.5.3 稳定性
归并排序是稳定的算法,它满足稳定算法的定义。
1.5.4 实现
#include<stdio.h>
#define ArrLen 20
void printList(int arr[], int len) {int i;for (i = 0; i < len; i++) {printf("%d\t", arr[i]);}
}
void merge(int arr[], int start, int mid, int end) {int result[ArrLen];int k = 0;int i = start;int j = mid + 1;while (i <= mid && j <= end) {if (arr[i] <= arr[j]){result[k++] = arr[i++];}else{result[k++] = arr[j++];}}if (i == mid + 1) {while(j <= end)result[k++] = arr[j++];}if (j == end + 1) {while (i <= mid)result[k++] = arr[i++];}for (j = 0, i = start ; j < k; i++, j++) {arr[i] = result[j];}
}void mergeSort(int arr[], int start, int end) {if (start >= end)return;int mid = ( start + end ) / 2;mergeSort(arr, start, mid);mergeSort(arr, mid + 1, end);merge(arr, start, mid, end);
}int main()
{int arr[] = {4, 7, 6, 5, 2, 1, 8, 2, 9, 1};mergeSort(arr, 0, 9);printList(arr, 10);system("pause");return 0;
}
1.6 常用排序算法总结
二、查找算法
2.1 二分查找法
2.1.1 基本逻辑
二分查找(Binary Search),也叫做折半查找,是一种在有序数组中查找某一特定元素的查找算法。查找过程从数组的中间元素开始,如果中间元素正好是要查找的元素,则查找过程结束;如果某一特定元素大于或者小于中间元素,则在数组大于或小于中间元素的那一半中查找,而且跟开始一样从中间元素开始比较。如果在某一步骤数组为空,则代表找不到。
这种查找算法每一次比较都使查找范围缩小一半。
{5,13,19,21,37,56,64,75,80,88,92}
采用折半查找算法查找关键字为 21 的过程为:
-
找到中间值
-
21比中间值小,则在左半部分找中间值
-
比中间值大,则在右半部分找中间值
2.1.2 时间复杂度
折半查找每次把搜索区域减少一半,时间复杂度为 O(logn) 空间复杂度:O(1)
2.1.3 实现
递归实现:
#include<stdio.h>
#define SIZE 10
typedef int ElemType;
int refind(ElemType *data,int begin,int end,ElemType num);
int main(void){ElemType data[SIZE]={10,20,30,40,50,60,70,80,90,100};ElemType num;for(int i = 0;i<SIZE;i++)printf("%d ",data[i]);printf("\n请输入要查找的数据:\n");scanf("%d",&num);int flag = refind(data,0,SIZE,num);printf("位置为:%d\n",flag);return 0;
}
/
//递归
int refind(ElemType *data,int begin,int end,ElemType num)
{if(begin > end){printf("没找到\n");return -1;}int mid = (begin+end)/2;if(data[mid] == num){return mid;}else if(data[mid] <= num)return refind(data,mid+1,end,num);elsereturn refind(data,begin,mid-1,num);
}
非递归实现:
#include <stdio.h>int bin_search( int str[], int n, int key )
{int low, high, mid;low = 0;high = n-1;while( low <= high ){mid = (low+high)/2;if( str[mid] == key ){return mid; // 查找成功}if( str[mid] < key ){low = mid + 1; // 在后半序列中查找}if( str[mid] > key ){high = mid - 1; // 在前半序列中查找}}return -1; // 查找失败
}int main()
{int str[11] = {1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89};int n, addr;printf("请输入待查找的关键字: ");scanf("%d", &n);addr = bin_search(str, 11, n);if( -1 != addr ){printf("查找成功, 关键字 %d 所在的位置是: %d\n", n, addr);}else{printf("查找失败!\n");}return 0;
}
2.2 不使用排序查找数组中第二大的值
2.2.1 思路
-
定义最大值max初始化为a[0],第二大值为sec,遍历数组,如果数组元素比max大就更新,max=a[i],sec记录上一次max的值;
-
如果数组元素不大于max,再将数组元素和sec判断,如果数组元素a[i]大于sec,则更新sec,sec=a[i]
2.2.2 代码实现
int secondbig(int data[],int N)
{int max=data[0],sec=0;for(int i=1;i<N;i++){if(max<data[i]){sec=max;max=data[i];}else{if(data[i]>sec){sec=data[i];}}} return sec;
}int main()
{int a[]={12,34,45,3,15,7,6,10};int sec = secondbig(a,8);return 0;
}