第二章 递归算法
功能:1.根据先序和中序数组,递归创建一个二叉树。
2.在调用完毕后递归释放二叉树的所有结点。
3.复制二叉树,从bt到bt1
4.输出从根节点到目标结点路径。
以下为代码部分。
#include <stack>
#include <vector>
#include <string>
#include <iostream>
#include <malloc.h>
using namespace std;
typedef int ElemType;
typedef struct node {
ElemType data; // 数据元素
struct node* lchild; // 指向左孩子结点
struct node* rchild; // 指向右孩子结点
} *BTNode;
BTNode CreateBT(ElemType a[], ElemType b[], int n)
{
int k;
if (n <= 0) { // 递归出口
return nullptr;
}
ElemType root = a[0]; // 根节点值
BTNode bt = (BTNode)malloc(sizeof(node)); // 创建根节点
bt->data = root;
bt->lchild = nullptr;
bt->rchild = nullptr;
for (k = 0; k < n; k++) { // 遍历中序数组,查找根结点位置
if (root == b[k]) {
break;
}
}
bt->lchild = CreateBT(a + 1, b, k); // 递归创建左子树
bt->rchild = CreateBT(a + k + 1, b + k + 1, n - k - 1); // 递归创建右子树
return bt;
}
void DestroyBT(BTNode& bt) // 递归释放二叉树所有结点
{
if (bt) { // 判断bt结点是否存在
DestroyBT(bt->lchild); // 递归释放左子树
DestroyBT(bt->rchild); // 递归释放右子树
free(bt); // 具体释放结点
}
}
void CopyBT(BTNode bt, BTNode& bt1) // 递归复制二叉树
{
if(!bt)
bt1 = nullptr;
else
{
bt1 = (BTNode)malloc(sizeof(node));
bt1->data = bt->data;
CopyBT(bt->lchild, bt1->lchild); // 递归复制左子树
CopyBT(bt->rchild, bt1->rchild); // 递归复制右子树
}
}
bool FindxPath(BTNode bt, ElemType x, vector<ElemType> &path) // 求根节点到x结点的逆向路径
{
if(!bt)
return false;
if(bt->data == x) // 找到x结点,则将结点值添加到path数组中,并返回真。
{
path.emplace_back(x);
return true;
}
else if(FindxPath(bt->lchild, x, path) || FindxPath(bt->rchild, x, path)) // 具体递归入口
{
path.emplace_back(bt->data); // 添加结点值,并返回true。
return true;
}
}
int main()
{
int a[7] = {1, 2, 4, 5, 3, 6, 7};
int b[7] = {4, 2, 5, 1, 6, 3, 7};
vector<ElemType> vt;
BTNode BT = CreateBT(a, b, 7);
BTNode BT1;
CopyBT(BT, BT1);
stack<BTNode> s;
s.push(BT1);
BTNode cur;
vector<ElemType> v;
FindxPath(BT1, 6, vt);
while (!s.empty()) {
cur = s.top();
s.pop();
v.emplace_back(cur->data);
if (cur->rchild) {
s.push(cur->rchild);
}
if (cur->lchild) {
s.push(cur->lchild);
}
}
DestroyBT(BT);
DestroyBT(BT1);
vector<ElemType>::iterator it = v.begin();
while (it < v.end()) {
cout << *it << " ";
it++;
}
cout << endl;
vector<ElemType>::iterator itt = vt.begin();
while (itt < vt.end()) {
cout << *itt << " ";
itt++;
}
cout << endl;
system("pause");
return 0;
}
第三章 分治法
Ⅰ快速排序
快速排序的基本步骤是
1. 选定一个元素作为基准值(一般是第一个元素);
2. 将比基准值小的元素放在基准值左边,比基准值大的元素放在基准值右边;
3. 对左右两个子序列进行递归操作,直到序列长度为1或0时排序完成。
现给出具体代码。
#include <iostream>
#include <vector>
using namespace std;
// 基本思想
/*
1. 选定一个元素作为基准值(一般是第一个元素);
2. 将比基准值小的元素放在基准值左边,比基准值大的元素放在基准值右边;
3. 对左右两个子序列进行递归操作,直到序列长度为1或0时排序完成。
*/
// 分治函数,将数组划分为左右两个子序列
int partition(vector<int>& nums, int left, int right)
{
// 将最左侧的元素作为基准值
int pivot = nums[left];
int i = left + 1, j = right;
while(1)
{
// 从左往右找到第一个比基准值大的数
while(i <= j && nums[i] < pivot) i++;
// 从右往左找第一个比基准值小的数
while(i <= j && nums[j] > pivot) j--;
// 如果i,j相遇,则退出循环
if(i>=j) break;
// 否则交换i和j对应元素
swap(nums[i], nums[j]);
i++;
j--;
}
// 将基准值归位即交换j对应的小于基准值的元素
swap(nums[left], nums[j]);
return j;
}
// 快排函数
void QuickSort(vector<int>& nums, int left, int right)
{
if(left >= right) return;
// 使用分治函数划分左右两个子序列
int pivotIndex = partition(nums, left, right);
QuickSort(nums, left, pivotIndex-1);
QuickSort(nums, pivotIndex+1, right);
}
int main()
{
vector<int> nums = {5, 3, 1, 4, 2};
int n = nums.size();
QuickSort(nums, 0, n - 1);
for (int x : nums) {
cout << x << " ";
}
cout << endl; // output: 1 2 3 4 5
return 0;
}
Ⅱ折半查找算法
基本思想如下:
1.将查找区间的左右端点分别初始化为数组的第一个和最后一个元素的下标。
2.如果左侧指针的值大于右侧指针,则表示查找失败,返回 -1。
3.取范围中间的元素,比较其值与目标值大小的关系。如果中间元素等于目标值,则返回其下标。如果中间元素大于目标值,则在左半边继续查找;如果中间元素小于目标值,则在右半边继续查找。
4.重复上述步骤直至找到目标元素或查找区间为空为止。
下面是二分查找的代码实现(仅适用于升序排列的数组):
#include <iostream>
#include <vector>
using namespace std;
int BinSearch(int *a, int low, int high, int k) // 二分查找算法
{
int mid;
if(low <= high) // 区间存在元素时
{
mid = (low + high) / 2; // 求出中间位值
if(a[mid] == k) // 查找成功,返回数组下标
return mid;
if(a[mid] > k)
return BinSearch(a, low, mid - 1, k); // 查找元素大于目标元素时,缩小区间为mid左侧
if(a[mid] < k)
return BinSearch(a, mid + 1, high, k); // 查找元素小于目标元素,缩小区间为mid右侧
}
else // 当前查找区间无元素
return -1;
}
int main()
{
int n = 10, i;
int k = 6;
int a[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
i = BinSearch(a, 0, n - 1, k);
if(i >= 0)
cout << a[i] << "==" << k << endl;
else
cout << "未找到" << endl;
system("pause");
return 0;
}
第四章 蛮力法
ⅠBF算法(字符串匹配)
BF算法是一种简单暴力的字符串匹配算法,BF算法的原理是在主串中逐个匹配子串,如果匹配失败,则将主串的指针向后移动一个位置,继续匹配,直到找到匹配的子串或者主串遍历完毕。
以下为代码示例。
#include <iostream>
#include <string>
using namespace std;
int BF(string s, string pattern)
{
int i = 0; //指向主串
int j = 0; //指向模式串
while(i < s.length() && j < pattern.length()){
if(s[i] == pattern[j]) // 当前字符匹配,则继续匹配下一字符
{
i++;
j++;
}else{ //否则i指针回退,j指针重新指向开始位置
i = i-j+1;
j = 0;
}
}
if(j == pattern.length()){
// 如果j指针指向了模式串的结尾位置,则返回匹配成功的起始位置
return i - j;
}
return -1; // 否则返回-1表示匹配失败
}
int main(){
string s = "hello world";
string pattern = "wor";
int res = BF(s, pattern);
cout << res <<endl; //6则成功
return 0;
}
Ⅱ0/1背包问题
首先枚举每个物品的选中状态,对于每个选中状态,我们遍历所有物品,计算总重量和总价值。如果总重量不超过背包承重,则更新最大总价值。由于我们需要枚举所有物品的选中状态,因此算法的时间复杂度为 O(2^n)。当物品数量较大时,蛮力法逐渐变得不切实际。
以下为算法实例。
#include <iostream>
#include <vector>
using namespace std;
/*
* 背包问题,蛮力法解法
* - items:物品重量数组
* - values:物品价值数组
* - n:物品数量
* - w:背包总承重
* 返回值:背包中物品的最大总价值
*/
int knapsack(vector<int>& items, vector<int>& values, int n, int w) {
int max_value = 0;
// 枚举所有物品的选中状态
for (int i = 0; i < (1 << n); i++) {
int weight = 0;
int value = 0;
// 根据当前选中状态计算总重量及总价值
for (int j = 0; j < n; j++) {
if ((i >> j) & 1) { // 第j个物品被选中
weight += items[j];
value += values[j];
}
}
// 如果总重量不超过背包承重,更新最大总价值
if (weight <= w) {
max_value = max(max_value, value);
}
}
return max_value;
}
int main() {
vector<int> items = {2, 2, 4, 6, 3}; // 物品重量数组
vector<int> values = {3, 4, 8, 9, 6}; // 物品价值数组
int n = items.size(); // 物品数量
int w = 9; // 背包总重量
int res = knapsack(items, values, n, w); // 计算最大总价值
cout << "Max value in backpack: " << res << endl; // 输出结果
return 0;
}