(一)排序
排序算法几乎是面试最常考的算法题目.
参考:
- 《STL源码剖析》6.7.9 sort 章节
STL的 sort 算法,数据量大时采用Quick Sort,分段递归排序;一旦分段的数据量小于某个门槛,为避免Quick Sort 的递归调用带来过大的额外负荷,就改用 Insertion Sort.
1. 冒泡排序
冒泡排序是以双层循环的形式:外循环遍历整个序列,每次迭代决定出一个子区间,不断缩小;内循环遍历子区间,将子区间内的每个“逆转对”倒转过来。算法复杂的为O(N^2)。
void BubbleSort(std::vector<int> &v){
int len = v.size();
for (int i = len - 1; i >= 0; --i){
for (int j = 0; j < i; j++){
if (v[j] > v[j + 1]){
int tmp = v[j];
v[j] = v[j + 1];
v[j + 1] = tmp;
}
}
}
}
// test
void BubbleSortTest(){
std::vector<int> v = { 1, 5, 3, 7, 9, 2, 5 };
BubbleSort(v);
for (auto e : v){
std::cout << e << std::endl;
}
}
2. 插入排序(Insertion Sort)
插入排序是以双层循环的形式:外循环遍历整个序列,每次迭代决定出一个子区间;内循环遍历子区间,将子区间内的每个“逆转对”倒转过来。算法复杂的为O(N^2)。
C++实现
#include <vector>
#include <iostream>
void InsertionSort(std::vector<int> &v){
int len = v.size();
int i = 1, j = 0;
for (; i < len; ++i){
int tmp = v[i];
for (j=i-1; j >= 0 && v[j] > tmp; --j){
v[j+1] = v[j];
}
v[j+1] = tmp;
}
}
// test
void InsertionSortTest()
{
std::vector<int> v = { 1, 5, 3, 7, 9, 2, 5 };
InsertionSort(v);
for (auto e : v){
std::cout << e << std::endl;
}
}
python实现
def insert_sort(nums):
length = len(nums)
for j in range(1, length):
temp = nums[j]
for i in range(j-1,-1,-1):
if nums[i]>=temp:
nums[i+1]=nums[i]
else:
break
nums[i+1]=temp
arr = [1,4,2,5,4,8,4]
sl = arr[:]
insert_sort(sl)
print(sl)
3. 快速排序(Quick Sort)
大数据量的情况下有许多排序算法可供选择,Quick Sort 正如其名,是目前已知最快的排序法(大数据量的情况下),平均复杂度为O(NlogN),最坏情况下达O(N^2),可通过 media-of-three 或 random 选取pivot 方式,将最坏情况推进到O(NlgN)。
Quick Sort 算法,精神在于将大区间分割为小区间,分段排序:
- 如果S的元素个数为0或1,结束;
- 取S中的任何一个元素,当做枢轴(pivot);
- 将S分割为L/R(左/右)两段,使L内的每一个元素都小于或等于pivot,R内的每一个元素都大于或等于pivot;
- 对LR递归执行QuickSort。
Patition分割方法:
令头端迭代器first向尾部移动,尾端迭代器last向头部移动。当*first
大于或等于pivot时停下来,当*last
小于或等于pivot时也停下来,然后校验两个迭代器是否交错,如果first<last,将两元素互换,然后各自调整位置(向中央逼近),再继续相同的行为。
#include <vector>
#include <iostream>
int Partition(std::vector<int> &v, int first, int last){
int pivot = v[first];
while (first < last){
while (first<last && v[first] < pivot){
first++;
}
while (last > first && !(v[last] < pivot)){
last--;
}
int tmp = v[first];
v[first] = v[last];
v[last] = tmp;
first++;
last--;
}
return --first;
}
void QuickSort(std::vector<int> &v, int first, int last){
if (first >= last){
return;
}
int mid = Partition(v, first, last);
QuickSort(v, first, mid);
QuickSort(v, mid + 1, last);
}
void QuickSort(std::vector<int> &v){
int len = v.size();
QuickSort(v, 0, len - 1);
}
// test
void QuickSortTest(){
std::vector<int> v = { 1, 5, 3, 7, 9, 2, 5 };
QuickSort(v);
for (auto e : v){
std::cout << e << std::endl;
}
}
4. 归并排序(Merge Sort)
有一个算法题:将两个有序区间归并成一个有序区间。
基于这个想法,我们可以利用“分而治之”(devide and conquer)的概念,以各个击破的方式来对一个区间进行排序。首先,将区间对半分割,左右两端各自排序,再利用merge重新组合为一个完整的有序序列。对半分割的操作可以递归进行,直到每一小段的长度为0或1。
Merge Sort 的复杂度为O(NlgN),虽然这和Quick Sort是一样的,但因为Merge Sort 需要额外的内存,而且在内存之间移动(复制)数据也会耗费不少时间,所以Merge Sort 的效率比不上 Quick Sort。
实现简单,概念简单,是Merge Sort的两大优点。
#include <vector>
#include <iostream>
void Merge(std::vector<int> &v, int first, int mid, int last){
std::vector<int> tmp;
int first1 = first;
int first2 = mid + 1;
while (first1 <= mid && first2 <= last){
if (v[first1] < v[first2]){
tmp.push_back(v[first1]);
first1++;
}
else{
tmp.push_back(v[first2]);
first2++;
}
}
while (first1 <= mid){
tmp.push_back(v[first1]);
first1++;
}
while (first2 <= last){
tmp.push_back(v[first2]);
first2++;
}
for (auto e : tmp){
v[first++] = e;
}
}
void MergeSort(std::vector<int> &v, int first, int last)
{
if (first >= last){
return;
}
int mid = (first + last) / 2;
MergeSort(v, first, mid);
MergeSort(v, mid + 1, last);
Merge(v, first, mid, last);
}
void MergeSort(std::vector<int> &v){
int len = v.size();
MergeSort(v, 0, len-1);
}
// test
void MergeSortTest(){
std::vector<int> v = { 1, 5, 3, 7, 9, 2, 5 };
MergeSort(v);
for (auto e : v){
std::cout << e << std::endl;
}
}