文章目录
- 一. 线性表
- 1. 逆置顺序表所有元素
- 2. 删除线性链表中数据域为 item 的所有结点
- 3. 逆转线性链表(递归(快速解题)+非递归)
- 4. 复制线性链表(递归)
- 5. 将两个按值有序排列的非空线性链表合并为一个按值有序的线性链表
- 二. 树
- 1. 建立二叉树(从数组获取数据,递归+非递归)
- 2. 二叉树的先序遍历(非递归算法)
- 3. 二叉树的中序遍历(非递归算法)
- 4. 二叉树的后序遍历(非递归算法,难点)
- 5. 二叉树层序遍历
- 6. 求二叉树的深度(递归算法)
- 7. 求二叉树的深度(非递归算法)
- 8. 求结点所在层次(非递归)
- 9. 交换二叉树中所有结点的左右子树的位置(递归)
- 10. 交换二叉树中所有结点的左右子树的位置(非递归)
- 11. 删除二叉树中以某个结点为根结点的子树
- 三. 查找
- 1. 顺序查找的递归算法
- 2. 折半查找
- 3. 折半查找的递归算法
- 4. 在按值递增排列且长度为 n 的线性表中折半查找并插入一元素
- 5. 在按值递增排列且长度为 n 的线性表中折半查找值不小于 key 的最小元素
- 四. 排序
- 1. 插入排序
- 2. 折半插入排序
- 3. 冒泡排序
- 4. 选择排序
- 5. 快速排序
- 6. 堆排序
这些算法主要面向数据结构代码填空,算法设计题,复试上机,复试面试
其中这些代码是最基础需要掌握的部分
这篇博客的代码部分经过测试,如果你遇到代码的问题,可以联系我:
代码仓库地址
一. 线性表
1. 逆置顺序表所有元素
算法思想:第一个元素和最后一个元素对调,第二个元素和倒数第二个元素对调,……,依此类推。
#include <vector>
using namespace std;
/**
using c++11
*/
void PrintVector(const vector<int> &vet)
{
for (auto &val : vet)
{
cout << val << " ";
}
cout << endl;
}
void reverse(vector<int> &vet)
{
int left = 0;
int right = vet.size() - 1;
while (left < right)
{
swap(vet[left], vet[right]);
left += 1;
right -= 1;
}
}
int main(int argc, char const *argv[])
{
vector<int> vet = {1, 2, 3, 4, 5};
reverse(vet);
PrintVector(vet);
return 0;
}
2. 删除线性链表中数据域为 item 的所有结点
算法思想:先从链表的第 2 个结点开始,从前往后依次判断链表中的所有结点是否满足条件,若某个结点的数据域为 item,则删除该结点。最后再回过头来判断链表中的第 1 个结点是否满足条件,若满足则将其删除。
相当于把链表第一个节点当作头节点,减小特殊情况的考虑
// #include <list>
#include <vector>
#include <iostream>
using namespace std;
struct Node
{
int val;
Node *next;
Node(int _val) : val(_val), next(nullptr) {}
};
typedef struct Node ListNode;
using namespace std;
/**
using C++11
*/
ListNode *InitList(const vector<int> &vet)
{
ListNode *head = new ListNode(-1);
Node *tail = head;
for (int i = 0; i < vet.size(); i++)
{
tail->next = new Node(vet[i]);
tail = tail->next;
}
ListNode *next = head->next;
delete head;
return next;
}
void PurgeItem(ListNode *list, int val)
{
// 空节点返回
if (list == nullptr)
{
return;
}
Node *prev = list;
Node *cur = prev->next;
while (cur != nullptr)
{
Node *next = cur->next;
if (cur->val == val)
{
prev->next = cur->next;
delete cur;
cur = next;
}
else
{
prev = cur;
cur = next;
}
}
// 验证第一个节点
if (list->val == val)
{
ListNode *head = list->next;
delete list;
list = head;
}
// 打印测试
while (list != nullptr)
{
cout << list->val << " ";
list = list->next;
}
}
int main(int argc, char const *argv[])
{
// ListNode *list = InitList({2, 2, 2, 2, 2});
ListNode *list = InitList({2, 2, 2, 3, 4});
PurgeItem(list, 2);
return 0;
}
3. 逆转线性链表(递归(快速解题)+非递归)
非递归思路:对头节点进行头插法逆置链表,注意在开始时初始化需要将头节点的下一个节点置空
#include <vector>
#include <iostream>
using namespace std;
struct Node
{
int val;
Node *next;
Node(int _val) : val(_val), next(nullptr) {}
};
typedef struct Node ListNode;
ListNode *InitList(const vector<int> &vet)
{
ListNode *head = new ListNode(-1);
Node *tail = head;
for (int i = 0; i < vet.size(); i++)
{
tail->next = new Node(vet[i]);
tail = tail->next;
}
ListNode *next = head->next;
delete head;
return next;
}
// 递归解法
ListNode *reverse(ListNode *head)
{
if (head == nullptr || head->next == nullptr)
{
return head;
}
ListNode *next = reverse(head->next);
head->next->next = head;
head->next = nullptr;
return next;
}
// 非递归解法
ListNode *reverse_display(ListNode *head)
{
// 对头节点进行头插法
ListNode *newHead = head;
ListNode *node = head->next;
newHead->next = nullptr;
while (node != nullptr)
{
ListNode *next = node->next;
node->next = newHead;
newHead = node;
node = next;
}
return newHead;
}
void PrintList(ListNode *list)
{
while (list != nullptr)
{
cout << list->val << " ";
list = list->next;
}
cout << endl;
}
int main(int argc, char const *argv[])
{
ListNode *list = InitList({1, 2, 2, 3, 4});
list = reverse(list);
PrintList(list);
list = reverse_display(list);
PrintList(list);
return 0;
}
4. 复制线性链表(递归)
#include <vector>
#include <iostream>
using namespace std;
struct Node
{
int val;
Node *next;
Node(int _val) : val(_val), next(nullptr) {}
};
typedef struct Node ListNode;
ListNode *InitList(const vector<int> &vet)
{
ListNode *head = new ListNode(-1);
Node *tail = head;
for (int i = 0; i < vet.size(); i++)
{
tail->next = new Node(vet[i]);
tail = tail->next;
}
ListNode *next = head->next;
delete head;
return next;
}
void PrintList(ListNode *list)
{
while (list != nullptr)
{
cout << list->val << " ";
list = list->next;
}
cout << endl;
}
ListNode *CopyList(ListNode *src)
{
ListNode *dst = nullptr;
if (src == nullptr)
{
return nullptr;
}
else
{
dst = new ListNode(src->val);
dst->next = CopyList(src->next);
return dst;
}
}
int main(int argc, char const *argv[])
{
ListNode *list = InitList({1, 2, 2, 3, 4});
ListNode *copy = CopyList(list);
PrintList(copy);
return 0;
}
5. 将两个按值有序排列的非空线性链表合并为一个按值有序的线性链表
#include <vector>
#include <iostream>
using namespace std;
struct Node
{
int val;
Node *next;
Node(int _val) : val(_val), next(nullptr) {}
};
typedef struct Node ListNode;
ListNode *InitList(const vector<int> &vet)
{
ListNode *head = new ListNode(-1);
Node *tail = head;
for (int i = 0; i < vet.size(); i++)
{
tail->next = new Node(vet[i]);
tail = tail->next;
}
ListNode *next = head->next;
delete head;
return next;
}
void PrintList(ListNode *list)
{
while (list != nullptr)
{
cout << list->val << " ";
list = list->next;
}
cout << endl;
}
ListNode *MergeList(ListNode *left, ListNode *right)
{
ListNode *head = new ListNode(-1);
ListNode *tail = head;
while (left != nullptr && right != nullptr)
{
if (left->val < right->val)
{
tail->next = left;
left = left->next;
}
else
{
tail->next = right;
right = right->next;
}
tail = tail->next;
}
tail->next = left == nullptr ? right : left;
return head->next;
}
int main(int argc, char const *argv[])
{
ListNode *left = InitList({1, 2, 3, 4, 5});
ListNode *right = InitList({2, 3, 4, 8});
PrintList(MergeList(left, right));
return 0;
}
二. 树
1. 建立二叉树(从数组获取数据,递归+非递归)
注意,这里使用了更直观的打印二叉树的方式,在考研中不需要掌握。所以这里会把二叉树打印结果截图放进来
非递归创建树的思路是:先创建节点,将节点保存起来,最后修改指针即可
#include <iostream>
#include <vector>
using namespace std;
struct TreeNode
{
int val;
TreeNode *left;
TreeNode *right;
TreeNode(int _val) : val(_val), left(nullptr), right(nullptr) {}
};
// 更好的二叉树打印(考研不需要掌握,为了打印树更直观),打印思路:https://blog.csdn.net/dodamce/article/details/130925799?spm=1001.2014.3001.5501
struct PrintCur
{
int deep(TreeNode *root)
{
if (root == nullptr)
{
return 0;
}
int left = deep(root->left);
int right = deep(root->right);
return max(left, right) + 1;
}
void dfs(TreeNode *root, vector<vector<string>> &ret, int row, int col, const int &deep)
{
if (root == nullptr)
{
return;
}
ret[row][col] = to_string(root->val);
if (root->left != nullptr)
dfs(root->left, ret, row + 1, col - (1 << (deep - row - 1)), deep);
if (root->right != nullptr)
dfs(root->right, ret, row + 1, col + (1 << (deep - row - 1)), deep);
}
void printTree(TreeNode *root)
{
int tree_deep = deep(root) - 1; // 根节点高度为0
int row = tree_deep + 1;
int col = (1 << row) - 1;
vector<vector<string>> ret(row, vector<string>(col, ""));
dfs(root, ret, 0, (col - 1) / 2, tree_deep);
for (int i = 0; i < ret.size(); i++)
{
for (int j = 0; j < ret[i].size(); j++)
{
cout << ret[i][j] << " ";
}
cout << endl;
}
}
};
class BTree
{
//下标从0开始,如果要求数组下标从1开始,则为2*i 2*i+1
// 递归创建树
TreeNode *CreateTree(const vector<int> &vet, int pos)
{
TreeNode *node = nullptr;
if (pos >= vet.size())
{
return nullptr;
}
node = new TreeNode(vet[pos]);
node->left = CreateTree(vet, pos * 2 + 1);
node->right = CreateTree(vet, pos * 2 + 2);
return node;
}
// 非递归创建树,重载
TreeNode *CreateTree(const vector<int> &vet)
{
vector<TreeNode *> nodes;
// 先创建节点,将节点保存起来,最后修改指针即可
for (int i = 0; i < vet.size(); i++)
{
nodes.push_back(new TreeNode(vet[i]));
}
// 修改节点指针的指向
for (int i = 0; i < nodes.size(); i++)
{
if (2 * i + 1 < nodes.size())
nodes[i]->left = nodes[2 * i + 1];
if (2 * i + 2 < nodes.size())
nodes[i]->right = nodes[2 * i + 2];
}
return nodes.front();
}
public:
PrintCur print;
TreeNode *root;
BTree(const vector<int> &vet)
{
// root = CreateTree(vet, 0);
root = CreateTree(vet);
}
void PrintTree()
{
print.printTree(root);
}
};
int main(int argc, char const *argv[])
{
BTree Tree({1, 2, 3, 4, 5});
Tree.PrintTree();
return 0;
}
2. 二叉树的先序遍历(非递归算法)
创建二叉树的方式见第一问,这里使用第一问创建的树来进行测试
先序遍历非递归思路:
-
若 p 所指结点不为空,则访问该结点,然后将该结点的地址入栈,然后再将 p 指向其左孩
子结点 -
若 p 所指向的结点为空,则从堆栈中退出栈顶元素(某个结点的地址),将 p 指向其右孩子
结点。 -
重复上述过程,直到 p = NULL 且堆栈为空,遍历结束
#include "../debug.hpp" //创建二叉树方法
#include <stack>
using namespace std;
void PreorderDisplay(TreeNode *root)
{
stack<TreeNode *> st;
while (!st.empty() || root != nullptr)
{
while (root != nullptr)
{
cout << root->val << " ";
st.push(root);
root = root->left;
}
root = st.top();
st.pop();
root = root->right;
}
}
int main(int argc, char const *argv[])
{
BTree tree({2, 4, 1, 5, 6});
tree.PrintTree();
PreorderDisplay(tree.root);
return 0;
}
3. 二叉树的中序遍历(非递归算法)
中序遍历非递归思路:
- 若 p 所指结点不为空,则将该结点的地址 p 入栈,然后再将 p 指向其左孩子结点;
- 若 p 所指向的结点为空,则从堆栈中退出栈顶元素(某个结点的地址)送 p,并访问该结点,然后再将 p 指
向该结点的右孩子结点。 - 重复上述过程,直到 p = NULL 且堆栈为空,遍历结束
#include "../debug.hpp" //创建二叉树方法
#include <stack>
using namespace std;
void InorderDisplay(TreeNode *root)
{
stack<TreeNode *> st;
while (!st.empty() || root != nullptr)
{
while (root != nullptr)
{
st.push(root);
root = root->left;
}
root = st.top();
st.pop();
cout << root->val << " ";
root = root->right;
}
}
int main(int argc, char const *argv[])
{
BTree tree({2, 4, 1, 5, 6});
tree.PrintTree();
InorderDisplay(tree.root);
return 0;
}
4. 二叉树的后序遍历(非递归算法,难点)
二叉树的后序遍历非递归算法思想:
- 当 p 指向某一结点时,不能马上对它进行访问,而要先访问它的左子树,因而要将此结点的地址入栈;
- 当其左子树访问完毕后,再次搜索到该结点时(该结点地址通过退栈得到),还不能对它进行访问,还需要先访问它的右子树,所以,再一次将该结点的地址入栈。只有当该结点的右子树访问完毕后回到该结点时,才能访问该结点。
- 为了标明某结点是否可以访问,引入一个标志变量flag,当 flag = 0 时表示该结点暂不访问,flag = 1 时表示该结点可以访问。flag 的值随同该结点的地址一起入栈和出栈。
- 因此,算法中设置了两个堆栈,其中 nodes 存放结点的地址,flags 存放标志变量 flag
#include "../debug.hpp" //创建二叉树
#include <stack>
using namespace std;
void PostOrderTraverse(TreeNode *node)
{
stack<TreeNode *> nodes;
stack<bool> flags;
while (!nodes.empty() || node != nullptr)
{
while (node != nullptr)
{
nodes.push(node);
flags.push(false);
node = node->left;
}
node = nodes.top();
nodes.pop();
bool flag = flags.top();
flags.pop();
if (flag == false)
{
// 不能访问这个节点,但是下次就可以访问这个节点
nodes.push(node);
flags.push(true);
node = node->right;
}
else
{
cout << node->val << " ";
node = nullptr; // 获取栈下一个元素,相当于向上递归
}
}
}
int main(int argc, char const *argv[])
{
BTree tree({2, 3, 4, 1, 5});
tree.PrintTree();
PostOrderTraverse(tree.root);
return 0;
}
5. 二叉树层序遍历
二叉树层序遍历思想:
- 设置一个队列,首先将根结点(的地址)入队列
- 依次从队列中退出一个元素,每退出一个元素,先访问该元素所指的结点
- 依次将该结点的左孩子结点(若存在的话)和右孩子结点(若存在的话)入队列。
- 如此重复下去,直到队列为空
#include "../debug.hpp"
#include <queue>
using namespace std;
void LayeredOrderTraverse(TreeNode *root)
{
queue<TreeNode *> nodes;
nodes.push(root);
while (!nodes.empty())
{
int size = nodes.size(); // 每层节点个数
for (int i = 0; i < size; i++)
{
TreeNode *node = nodes.front();
nodes.pop();
cout << node->val << " ";
if (node->left)
nodes.push(node->left);
if (node->right)
nodes.push(node->right);
}
cout << endl;
}
}
int main(int argc, char const *argv[])
{
BTree tree({4, 3, 5, 6, 3, 1});
tree.PrintTree();
LayeredOrderTraverse(tree.root);
return 0;
}
6. 求二叉树的深度(递归算法)
// C++递归计算二叉树的深度
#include "../debug.hpp"
int deep(TreeNode *root)
{
if (root == nullptr)
{
return 0;
}
int left = deep(root->left);
int right = deep(root->right);
return max(left, right) + 1;
}
int main(int argc, char const *argv[])
{
BTree tree({1, 2, 3, 4, 5, 6});
tree.PrintTree();
cout << deep(tree.root) << endl;
return 0;
}
7. 求二叉树的深度(非递归算法)
算法思想:
- 对二叉树进行遍历,遍历过程中依次记录各个结点所处的层次数以及当前已经访问过的结点所处的最大层次数。
- 每当访问到某个叶子结点时,将该叶子结点所处的层次数与最大层次数进行比较
- 若前者大于后者,则修改最大层次数为该叶子结点的层次数,否则不作修改。
- 遍历结束时,所记录的最大层次数即为该二叉树的深度。
本算法使用的是非递归的中序遍历算法(其它遍历顺序也可以)
// C++递归计算二叉树的深度
#include "../debug.hpp"
#include <stack>
using namespace std;
struct Data
{
TreeNode *node;
int deep;
Data(TreeNode *_node, int _deep) : node(_node), deep(_deep) {}
};
int deep(TreeNode *root)
{
stack<Data *> nodes;
if (root == nullptr)
{
return 0;
}
int curDeep = 1;
int deep = 1;
while (!nodes.empty() || root != nullptr)
{
while (root != nullptr)
{
nodes.push(new Data(root, curDeep));
root = root->left;
curDeep += 1;
}
Data *data = nodes.top();
nodes.pop();
root = data->node;
curDeep = data->deep;
if (root->left == nullptr && root->right == nullptr)
{
deep = max(deep, curDeep);
}
root = root->right;
curDeep += 1;
}
return deep;
}
int main(int argc, char const *argv[])
{
BTree tree({1, 2, 3, 4, 5, 6});
tree.PrintTree();
cout << deep(tree.root) << endl;
return 0;
}
8. 求结点所在层次(非递归)
算法思想:
- 采用后序遍历的非递归算法对二叉树进行遍历
- 遍历过程中对每一个结点判断其是否为满足条件的结点,若是满足条件的结点,则此时堆栈中保存的元素个数再加 1 即为该结点所在的层次。
也可以采用前序遍历,这里使用后序遍历
#include "../debug.hpp"
#include <stack>
struct Data
{
TreeNode *node;
bool flag;
Data(TreeNode *_node, bool _flag = false) : node(_node), flag(_flag) {}
};
int LayerNode(TreeNode *node, int target)
{
stack<Data *> nodes;
// 后续遍历
while (!nodes.empty() || node != nullptr)
{
while (node != nullptr)
{
nodes.push(new Data(node));
node = node->left;
}
Data *data = nodes.top();
nodes.pop();
node = data->node;
bool flag = data->flag;
delete data;
if (flag == false)
{
nodes.push(new Data(node, true));
node = node->right;
}
else
{
if (node->val == target)
{
return nodes.size() + 1;
}
else
{
node = nullptr;
}
}
}
return 0; // 没找到对应的节点
}
int main(int argc, char const *argv[])
{
BTree tree({1, 2, 3, 4, 5});
tree.PrintTree();
cout << LayerNode(tree.root, 4) << endl;
return 0;
}
9. 交换二叉树中所有结点的左右子树的位置(递归)
#include "../debug.hpp"
// 递归
TreeNode *ExchangeBT(TreeNode *root)
{
if (root == nullptr)
{
return nullptr;
}
TreeNode *left = ExchangeBT(root->left);
TreeNode *right = ExchangeBT(root->right);
root->left = right;
root->right = left;
return root;
}
int main(int argc, char const *argv[])
{
BTree tree({1, 2, 3, 4, 5});
tree.PrintTree();
PrintCur print;
print.printTree(ExchangeBT(tree.root));
return 0;
}
10. 交换二叉树中所有结点的左右子树的位置(非递归)
算法思想:按层次遍历二叉树,遍历过程中每当访问一个结点时,就将该结点的左右子树的位置对调
#include "../debug.hpp"
#include <queue>
#include <algorithm>
// 非递归
TreeNode *ExchangeBT(TreeNode *root)
{
queue<TreeNode *> nodes;
nodes.push(root);
while (!nodes.empty())
{
TreeNode *node = nodes.front();
nodes.pop();
swap(node->left, node->right);
if (node->left != nullptr)
nodes.push(node->left);
if (node->right != nullptr)
nodes.push(node->right);
}
return root;
}
int main(int argc, char const *argv[])
{
BTree tree({1, 2, 3, 4, 5});
tree.PrintTree();
PrintCur print;
print.printTree(ExchangeBT(tree.root));
return 0;
}
11. 删除二叉树中以某个结点为根结点的子树
算法思想:
- 先序遍历找到符合条件的结点(其它遍历方法亦可)
- 然后删除以该结点为根结点的子树。
- 最后把该结点的父结点的相应的指针域置为 NULL。
需在算法中设置一个指针变量用以指示当前结点的父结点。
#include "../debug.hpp"
#include <stack>
void Destroy(TreeNode *node)
{
if (node == nullptr)
return;
Destroy(node->left);
Destroy(node->right);
delete node;
}
TreeNode *DeleteSubtree(TreeNode *root, int val)
{
TreeNode *node = root;
stack<TreeNode *> nodes;
TreeNode *parent = nullptr;
while (!nodes.empty() || root != nullptr)
{
while (root != nullptr)
{
if (root->val == val)
{
if (parent == nullptr)
{
// 全部删除
Destroy(root);
return nullptr;
}
else
{
Destroy(root->left);
Destroy(root->right);
if (parent->left == root)
{
parent->left = nullptr;
}
else
{
parent->right = nullptr;
}
return node;
}
}
nodes.push(root);
parent = root;
root = root->left;
}
parent = nodes.top();
nodes.pop();
root = parent->right;
}
return root;
}
int main(int argc, char const *argv[])
{
BTree tree({1, 2, 3, 4, 5, 6, 7});
tree.PrintTree();
PrintCur print;
print.printTree(DeleteSubtree(tree.root, 3));
cout << "end" << endl;
return 0;
}
三. 查找
1. 顺序查找的递归算法
#include <vector>
#include <iostream>
using namespace std;
int Search(vector<int> &seq, int val, int pos)
{
if (pos >= seq.size())
{
return -1;
}
if (seq[pos] == val)
{
return pos;
}
return Search(seq, val, pos + 1);
}
int RecurSeqSearch(vector<int> &seq, int val)
{
return Search(seq, val, 0);
}
int main(int argc, char const *argv[])
{
vector<int> ret = {1, 2, 3, 4, 5};
cout << RecurSeqSearch(ret, -1) << endl;
return 0;
}
2. 折半查找
注意:折半查找数组必须有序
#include "../debug.hpp"
int BinSearch(vector<int> &array, int val)
{
int left = 0;
int right = array.size() - 1; //[]
while (left <= right)
{
int mid = (left + right) / 2;
if (array[mid] == val)
{
return mid;
}
else if (array[mid] > val)
{
right = mid - 1;
}
else
{
left = mid + 1;
}
}
// 没查找到
return -1;
}
int main(int argc, char const *argv[])
{
vector<int> ret = {1, 2, 3, 4, 5, 6};
PrintVector(ret);
cout << BinSearch(ret, 9);
return 0;
}
3. 折半查找的递归算法
#include "../debug.hpp"
int dfs(vector<int> &vet, int val, int left, int right)
{
if (left > right)
return -1;
int mid = (left + right) / 2;
if (vet[mid] == val)
return mid;
if (vet[mid] > val)
return dfs(vet, val, left, mid - 1);
else
return dfs(vet, val, mid + 1, right);
}
int RecurBinSearch(vector<int> &vet, int val)
{
return dfs(vet, val, 0, vet.size() - 1);
}
int main(int argc, char const *argv[])
{
vector<int> ret = {1, 2, 3, 4, 5, 6};
PrintVector(ret);
cout << RecurBinSearch(ret, 4);
return 0;
}
4. 在按值递增排列且长度为 n 的线性表中折半查找并插入一元素
#include "../debug.hpp"
// 使用折半插入值val,是vet保持有序
void BinInsert(vector<int> &vet, int val)
{
// 查找最相近val的值在vet中的位置
int left = 0;
int right = vet.size() - 1;
while (left <= right)
{
int mid = (left + right) / 2;
if (vet[mid] > val)
{
right = mid - 1;
}
else if (vet[mid] < val)
{
left = mid + 1;
}
else if (vet[mid] == val)
{
// 查找最相近val的值在vet中的位置
left = mid + 1;
}
}
// vet.insert(vet.begin() + left, val);
vet.resize(vet.size() + 1);
// 拷贝元素
for (int i = vet.size() - 1; i > left; i--)
{
vet[i] = vet[i - 1];
}
vet[left] = val;
}
int main(int argc, char const *argv[])
{
vector<int> ret = {1, 2, 3, 4, 5, 6};
PrintVector(ret);
BinInsert(ret, 0);
PrintVector(ret);
return 0;
}
5. 在按值递增排列且长度为 n 的线性表中折半查找值不小于 key 的最小元素
#include "../debug.hpp"
using namespace std;
int BinSearch(vector<int> &vet, int val)
{
int left = 0;
int right = vet.size() - 1;
while (left <= right)
{
int mid = (left + right) / 2;
if (vet[mid] < val)
{
left = mid + 1;
}
else if (vet[mid] > val)
{
right = mid - 1;
}
else
{
return mid;
}
}
if (left <= vet.size() - 1)
{
return left;
}
return -1;
}
int main(int argc, char const *argv[])
{
vector<int> ret = {1, 2, 3, 4, 6};
PrintVector(ret);
cout << BinSearch(ret, 5) << endl;
return 0;
}
四. 排序
1. 插入排序
算法思想:
- 第 i 趟插入排序为:在含有 i − 1 个元素的有序子序列中插入一个元素,使之成为含有 i个元素的有序子序列。
- 在查找插入位置的过程中,可以同时后移元素。整个过程为进行 n − 1 趟插入
即先将整个序列的第 1 个元素看成是有序的,然后从第 2 个元素起逐个进行插入,直到整个序列有序为止。
#include "../debug.hpp"
using namespace std;
void InsertSort(vector<int> &arr)
{
for (int i = 1; i < arr.size(); i++)
{
if (arr[i] < arr[i - 1])
{
int left = i - 1;
int val = arr[i];
while (left >= 0 && arr[left] > val)
{
arr[left + 1] = arr[left];
left -= 1;
}
arr[left + 1] = val;
}
}
}
int main(int argc, char const *argv[])
{
vector<int> arr = {4, 2, 5, 7, 0};
PrintVector(arr);
InsertSort(arr);
PrintVector(arr);
return 0;
}
2. 折半插入排序
算法思想:算法同直接插入排序,只不过使用折半查找的方法来寻找插入位置。
#include "../debug.hpp"
using namespace std;
int Search(vector<int> &vet, int left, int right, int val)
{
while (left <= right)
{
int mid = (left + right) / 2;
if (vet[mid] == val)
{
left = mid + 1; // 保证稳定性
}
else if (vet[mid] < val)
{
left = mid + 1;
}
else
{
right = mid - 1;
}
}
return left;
}
void BinInsertSort(vector<int> &vet)
{
for (int i = 1; i < vet.size(); i++)
{
if (vet[i] < vet[i - 1])
{
// 找插入的位置,大于val的最小值
int val = vet[i];
int left = Search(vet, 0, i, val);
int right = i;
while (left < right)
{
vet[right] = vet[right - 1];
right -= 1;
}
vet[left] = val;
}
}
}
int main(int argc, char const *argv[])
{
vector<int> arr = {4, 2, 4, 7, 0};
PrintVector(arr);
BinInsertSort(arr);
PrintVector(arr);
return 0;
}
3. 冒泡排序
设立一个标志变量 flag,flag = 0 表示有过交换元素的操作,flag = 1 表示没有过交换元素的操作.
在每一趟排序开始前,将 flag 置为 1,在排序过程中,只要有交换元素的操作,就及时将 flag 置为 0。
#include "../debug.hpp"
void BubbleSort(vector<int> &vet)
{
bool flag = true;
for (int i = 0; i < vet.size(); i++)
{
bool flag = true;
for (int j = 0; j < vet.size() - i - 1; j++)
{
if (vet[j] > vet[j + 1])
{
swap(vet[j], vet[j + 1]);
flag = false;
}
}
if (flag == true)
{
break;
}
}
}
int main(int argc, char const *argv[])
{
vector<int> arr = {4, 2, 4, 7, 0, 1, 3};
PrintVector(arr);
BubbleSort(arr);
PrintVector(arr);
return 0;
}
4. 选择排序
每次选择最大值放到已经排序的序列头部,知道所有的元素都执行过这个操作。
#include "../debug.hpp"
void SelectSort(vector<int> &vet)
{
for (int i = 0; i < vet.size(); i++)
{
int max = 0;
for (int j = 0; j < vet.size() - i; j++)
{
if (vet[max] < vet[j])
{
max = j;
}
}
swap(vet[max], vet[vet.size() - i - 1]);
}
}
int main(int argc, char const *argv[])
{
vector<int> arr = {4, 2, 4, 7, 0, 1, 3};
PrintVector(arr);
SelectSort(arr);
PrintVector(arr);
return 0;
}
5. 快速排序
算法思路:
- 在参加排序的序列中任意选择一个元素(通常称为分界元素或基准元素),把小于或等于分界元素的所有元素都移到分界元素的前面,把大于分界元素的所有元素都移到分界元素的后面(这里使用挖坑法,考研过程中也是使用的这个方法)
- 这样,当前参加排序的序列就被划分成前后两个子序列,其中前一个子序列中的所有元素都小于后一个子序列的所有元素,并且分界元素正好处于排序的最终位置上。
- 然后分别对这两个子序列递归地进行上述排序过程,直到所有元素都处于排序的最终位置上,排序结束
#include "../debug.hpp"
int Partition(vector<int> &vet, int left, int right)
{
int key = vet[left]; // 基准值
while (left < right)
{
// 起始left为坑
while (left < right && vet[right] >= key)
{
right -= 1;
}
swap(vet[left], vet[right]);
// right为坑
while (left < right && vet[left] <= key)
{
left += 1;
}
swap(vet[left], vet[right]);
}
vet[left] = key;
return left;
}
void QuickSort(vector<int> &vet, int left, int right)
{
if (left > right)
{
return;
}
int mid = Partition(vet, left, right);
QuickSort(vet, left, mid - 1);
QuickSort(vet, mid + 1, right);
}
void QuickSort(vector<int> &vet)
{
QuickSort(vet, 0, vet.size() - 1);
}
int main(int argc, char const *argv[])
{
vector<int> arr = {4, 2, 4, 7, 0, 1, 3};
PrintVector(arr);
QuickSort(arr);
PrintVector(arr);
return 0;
}
6. 堆排序
注意:我这个堆的下标从0开始
#include "../debug.hpp"
// 从小到大排列,建立大堆
void HeapAdjust(vector<int> &vet, int parent, int size)
{
// vet从begin开始调整为堆,堆的大小为size
int child = 2 * parent + 1;
while (child <= size)
{
if (child + 1 <= size)
{
if (vet[child] < vet[child + 1])
{
child += 1;
}
}
if (vet[parent] < vet[child])
swap(vet[parent], vet[child]);
parent = child;
child = 2 * parent + 1;
}
}
void HeapSort(vector<int> &vet)
{
for (int i = ((vet.size() - 1) - 1) / 2; i >= 0; i--)
{
HeapAdjust(vet, i, vet.size() - 1);
}
// PrintVector(vet);
int endPos = vet.size() - 1;
while (endPos >= 0)
{
swap(vet[0], vet[endPos]);
endPos -= 1;
HeapAdjust(vet, 0, endPos);
}
}
int main(int argc, char const *argv[])
{
vector<int> arr = {4, 2, 4, 7, 0, 1, 3};
PrintVector(arr);
HeapSort(arr);
PrintVector(arr);
return 0;
}