排序算法
先来个总结。
下面都是讲从小到大排序。
插入排序
对数组num的元素进行排序:从num[1]开始,每次循环查找插入位置,直到遍历完数组。(即每次循环相当于将数组分成了两部分,前半部分已排序,后半部分未排序,图中蓝色为有序区,绿色为无序区,橙色为当前待排序元素)
最好
O
(
n
)
O(n)
O(n),最坏
O
(
n
2
)
O(n^2)
O(n2),平均
O
(
n
2
)
O(n^2)
O(n2)。
void insertSort(vector<int>& num)
{
int i, j, temp;
for(i = 1; i < num.size(); ++i) {
temp = num[i]; // 当前待排序元素
for(j = i-1; j >= 0 && num[j] > num[i]; --j){
a[j+1] = a[j];
}
a[j+1] = temp; // 找到了待插入位置并将元素插入到该位置。
}
}
冒泡排序
外层循环n次,每次循环针对无序区相邻元素两两比较将无序区的最大元素沉底,同时有序区元素个数加1,无序区元素个数减1。(将数组分为有序区和无序区两部分,下图绿色为无序区,蓝色为有序区)
最好 O ( n ) O(n) O(n),最坏 O ( n 2 ) O(n^2) O(n2),平均 O ( n 2 ) O(n^2) O(n2)。
void bubbleSort(int arr[], int len)
{
for(int i = 0; i < n; ++i)
for(int j = 0; j < n-i-1; ++j) {
if (a[j] > a[j+1]) {
int temp = a[j];
a[j] = a[j+1];
a[j+1] = temp;
}
}
}
}
快速排序
每次排序选定一个主元,将待排序列分为两部分,左边都小于主元,右边都大于主元。
Partition 函数利用主元将序列分成两部分。
最好
O
(
n
l
o
g
n
)
O(nlogn)
O(nlogn),最坏
O
(
n
2
)
O(n^2)
O(n2),平均
O
(
n
l
o
g
n
)
O(nlogn)
O(nlogn)。
递归解法
void quickSort(int arr[],int first,int end)
{
if(first < end){
int pivot = Partition(arr, first, end);
quickSort(arr, first, pivot-1);
quickSort(arr,pivot+1,end);
}
}
int Partition(int arr[], int first, int end)
{
int i = first, j = end;
while(i < j){
while(i < j && arr[i] <= arr[j]) --j;
if(i < j) {
int temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}
while(i < j && a[i] <= a[j]) ++i;
if(i < j){
int temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}
}
return i; // 此时 i>=j
}
迭代法
迭代法需要用到栈,首先将0和len-1入栈,然后进入循环,出栈两个元素作为需要划分的数组区间,然后作一次划分,划分之后的两部分若元素个数大于1,则分别将起始和终点位置入栈,如此循环下去,直至栈为空。
void quickSort(int arr[], int len)
{
stack<pair<int,int> > s;
s.push(make_pair(0,len-1));
while(!s.empty()){
int start=s.top().first, end=s.top().second;
s.pop();
if(start<end){
int indexPivot = Partition(arr, start, end);
s.push(make_pair(start,indexPivot-1));
s.push(make_pair(indexPivot+1,end));
}
}
}
// 从左到右搜索的Partition 实现
int Partition(int arr[], int start, int end)
{
int pivotValue = arr[start]; // 取第一个元素为主元
int i=start; // i为左半部分的最后一个元素, i+1为右半部分第一个元素
for(int j=start+1; j<= end; ++j){
if(arr[j]<=pivotValue){ // 表明第j个元素应该是左半部分元素,
++i;
swap(arr[i],arr[j]); // 令右半部分第一个元素与其交换
}
}
swap(arr[start],arr[i]);
return i;
}
// 从两端搜索的Partition实现
int Partition(int arr[], int start, int end)
{
while(start<end){
while(start<end && arr[start]<=arr[end]) --end;
if(start<end)
swap(arr[start],arr[end]);
while(start<end && arr[start]<=arr[end]) ++start;
if(start<end)
swap(arr[start],arr[end]);
}
return start;
}
堆排序
首先构造堆,每次循环取出最大元素,然后将剩余部分调整成为堆。
可以发现,每调整成为堆一次(包括初始化堆),待排序列的最大元素被置于arr[0],然后将堆顶元素与当前堆的最后一个元素交换,继续调整剩余部分成为堆。
初始化堆时:从最后一个非叶子结点开始,令当前结点的孩子结点中的最大值与当前结点比较并判断是否交换,再把交换的子节点置为当前结点,继续向下调整堆;然后继续遍历下一个非叶子结点。
调整堆时:由于只将堆顶元素与最后一个元素交换,只需要从堆顶元素开始调整(其他的父结点与子节点都满足堆的定义),将堆顶元素的孩子结点最大值与堆顶元素比较,若交换,则令当前结点为其交换的孩子结点,继续向下调整堆。
最好
O
(
n
l
o
g
n
)
O(nlogn)
O(nlogn),最坏
O
(
n
l
o
g
n
)
O(nlogn)
O(nlogn)。
void heapSort(int arr[], int len)
{
int i;
for (i = len/2 -1; i >= 0; --i){
Heapify(arr, i, len); // 初始化堆:从最后一个非叶子结点开始调整堆
}
for(i = len-1; i >0; --i){ // 将堆顶元素【arr[0]】与当前堆的最后一个元素交换
int temp = arr[i];
arr[i] = arr[0];
arr[0] = temp;
Heapify(arr, 0, i); // 并将剩余部分调整为堆
}
}
void Heapify(int arr[], int first, int end){
int father = first;
int son = father*2 + 1;
while(son < end){ // 将当前结点与其孩子结点的更大值交换
if(son + 1 < end && arr[son] < arr[son+1]) ++son; // 记录孩子中的最大值
if(arr[father] > arr[son]) break;
else {
int temp = arr[father];
arr[father] = arr[son];
arr[son] = temp;
father = son; // 若交换,将当前父结点置为刚交换的子节点
son = 2*father+1;
}
}
}
归并排序
将数组一分为二,然后使得两部分分别有序,最后将有序的两部分合并。
最好 O ( n l o g n ) O(nlogn) O(nlogn),最坏 O ( n l o g n ) O(nlogn) O(nlogn)。
void Merge(int arr[], vector<int>& reg, int start, int end)
{
if (start >= end) return;
int len = end - start, mid = (len >> 1) + start;
int start1 = start, end1 = mid;
int start2 = mid+1, end2 = end;
Merge(arr, reg, start1, end1);
Merge(arr, reg, start2, end2);
int k = start;
while(start1 <= end1 && start2 <= end2)
reg[k++] = arr[start1] <= arr[start2] ? arr[start1++] : arr[start2++];
while(start1 <= end1)
reg[k++] = arr[start1++];
while(start2 <= end2)
reg[k++] = arr[start2++];
for(k=start;k<=end;++k)
arr[k]=reg[k];
}
void MergeSort(int arr[], int len)
{
vector<int> reg(len);
Merge(arr, reg, 0, len-1);
}
查找算法
二分查找
时间复杂度为 O ( l o g n ) O(logn) O(logn)
// 迭代法
// 二分查找的迭代不需要用到栈,因为每次只取一部分,另一部分被舍弃
int BinarySearch(int arr[], int start, int end, int key)
{
while(start<=end){
int mid = (start+end)/2;
if(key<arr[mid])
end=mid-1;
else if(key>arr[mid])
start=mid+1;
else
return mid;
}
return -1;
}
// 递归法
int BinarySearch(int arr[], int start, int end, int key)
{
if(start>end) return -1;
int mid = (start+end)/2;
if(key<arr[mid])
return BinarySearch(arr, start, mid-1, key);
if(key>arr[mid])
return BinarySearch(arr, mid+1, end, key);
return mid;
}
参考文献:
[1] 常见排序算法C++总结
树的遍历
先序遍历
// 递归法
void preOrderTraverseRecursively(BiNode* pRoot){
if(pRoot!=nullptr){
cout << pRoot->value << " ";
preOrderTraverseRecursively(pRoot->left);
preOrderTraverseRecursively(pRoot->right);
}
}
// 迭代法
void preOrderTraverseIteratively(BiNode* pRoot){
stack<BiNode* > s;
BiNode* p=pRoot;
while(p!=nullptr || !s.empty()){
while(p!=nullptr){
cout<<p->value<<" ";
s.push(p);
p=p->left;
}
if(!s.empty()){
p=s.top();
s.pop();
p=p->right;
}
}
}
void preOrderIteratively(BinaryTreeNode* H) {
// 利用栈的循环实现方法
if (H == NULL)
return;
stack<BinaryTreeNode*> mStack;
mStack.push(H); //把根节点压入栈中
while (!mStack.empty()) {
BinaryTreeNode* pcur = mStack.top();
mStack.pop(); //栈顶元素出栈
cout << pcur->value << " ";
if (pcur->right != NULL)
mStack.push(pcur->right); // 先将右子树入栈
if (pcur->left != NULL)
mStack.push(pcur->left); // 再将左子树入栈
}
}
中序遍历
// 递归法
void inOrderTraverseRecursively(BiNode* pRoot){
if(pRoot!=nullptr){
inOrderTraverseRecursively(pRoot->left);
cout<<pRoot->value<<" ";
inOrderTraverseRecursively(pRoot->right);
}
}
// 迭代法
void inOrderTraverseIteratively(BiNode* pRoot){
stack<BiNode*> s;
BiNode* p=pRoot;
while(p!=nullptr || !s.empty()){
while(p!=nullptr){
s.push(p);
p=p->left;
}
if(!s.empty()){
p=s.top();
s.pop();
cout<<p->value<<" ";
p=p->right;
}
}
}
void inOrderIteratively(BinaryTreeNode* H) {
if (H == NULL)
return;
BinaryTreeNode* p = H; // 指针p指向下一个待访问的结点
stack<BinaryTreeNode*> mStack;
while (p != NULL ) { //
if (p->left != NULL) { // 若p存在左子树,p入栈,p指向p的左子树
mStack.push(p);
p = p->left;
}
else { // 若p不存在左子树,输出p, 然后将p 指向p的右子树
cout << p->value << " ";
p = p->right;
while (p == NULL && !mStack.empty()) { //若p为空且栈非空,栈顶元素出栈,并输出;将p设置为输出结点的右孩子
p = mStack.top();
cout << p->value << " ";
mStack.pop();
p = p->right;
}
}
}
}
后序遍历
// 递归法
void postOrderTraverseRecursively(BiNode* pRoot){
if(pRoot!=nullptr){
postOrderTraverseRecursively(pRoot->left);
postOrderTraverseRecursively(pRoot->right);
cout<<pRoot->value<<" ";
}
}
// 迭代法
typedef struct {
BiNode* BTNode; // 记录结点
bool isFirst; // 记录结点是否第一次遍历到
}TempNode;
void postOrderTraverseIteratively(BiNode* pRoot){
stack<TempNode*> s;
BiNode* p=pRoot;
TempNode* tmp=nullptr;
while(p!=nullptr || !s.empty()){
while(p!=nullptr){
tmp=new TempNode;
tmp->BTNode=p;
tmp->isFirst=true;
s.push(tmp);
p=p->left;
}
if(!s.empty()){
tmp=s.top();
s.pop();
if(tmp->isFirst==true){
tmp->isFirst=false;
s.push(tmp);
p=tmp->BTNode->right;
}
else{
cout<<tmp->BTNode->value<<" ";
p=nullptr;
}
}
}
}
void postOrderIteratively(BinaryTreeNode* H) {
if (H == NULL)
return;
stack<BinaryTreeNode*> mStack;
BinaryTreeNode* p, *r=NULL; // p指向下一个要访问的结点,r代表上一个访问的结点
mStack.push(H); // 将根节点入栈
//直到栈空结束循环
while (!mStack.empty()) {
p = mStack.top(); // 将p设置为栈顶元素
if ((p->left == NULL&&p->right == NULL) || (r != NULL && (p->left == r || p->right == r))) {
// 若p无左右子树或者其左右子树被访问过
// 则出栈并输出该结点,并令r为该结点
cout << p->value << " ";
mStack.pop();
r = p;
}
else {
// 将右子树,左子树依次入栈
if (p->right != NULL) {
mStack.push(p->right);
}
if (p->left != NULL) {
mStack.push(p->left);
}
}
}
}
层次遍历
void levelOrder(BinaryTreeNode* H) {
// 采用队列进行层次遍历
if (H == NULL)
return;
queue<BinaryTreeNode*> mQueue;
mQueue.push(H);
while (!mQueue.empty()) {
BinaryTreeNode* node = mQueue.front();
mQueue.pop();
cout << node->value << " ";
if (node->left != NULL)
mQueue.push(node->left);
if (node->right != NULL)
mQueue.push(node->right);
}
}