考研/面试 数据结构大题必会代码(理解+记忆,实现使用C++,STL库)

news2025/2/23 13:22:37

文章目录

  • 一. 线性表
    • 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. 二叉树的先序遍历(非递归算法)

创建二叉树的方式见第一问,这里使用第一问创建的树来进行测试

先序遍历非递归思路:

  1. 若 p 所指结点不为空,则访问该结点,然后将该结点的地址入栈,然后再将 p 指向其左孩
    子结点

  2. 若 p 所指向的结点为空,则从堆栈中退出栈顶元素(某个结点的地址),将 p 指向其右孩子
    结点。

  3. 重复上述过程,直到 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. 二叉树的中序遍历(非递归算法)

中序遍历非递归思路:

  1. 若 p 所指结点不为空,则将该结点的地址 p 入栈,然后再将 p 指向其左孩子结点;
  2. 若 p 所指向的结点为空,则从堆栈中退出栈顶元素(某个结点的地址)送 p,并访问该结点,然后再将 p 指
    向该结点的右孩子结点。
  3. 重复上述过程,直到 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. 二叉树的后序遍历(非递归算法,难点)

二叉树的后序遍历非递归算法思想:

  1. 当 p 指向某一结点时,不能马上对它进行访问,而要先访问它的左子树,因而要将此结点的地址入栈;
  2. 当其左子树访问完毕后,再次搜索到该结点时(该结点地址通过退栈得到),还不能对它进行访问,还需要先访问它的右子树,所以,再一次将该结点的地址入栈。只有当该结点的右子树访问完毕后回到该结点时,才能访问该结点。
  3. 为了标明某结点是否可以访问,引入一个标志变量flag,当 flag = 0 时表示该结点暂不访问,flag = 1 时表示该结点可以访问。flag 的值随同该结点的地址一起入栈和出栈。
  4. 因此,算法中设置了两个堆栈,其中 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. 二叉树层序遍历

二叉树层序遍历思想:

  1. 设置一个队列,首先将根结点(的地址)入队列
  2. 依次从队列中退出一个元素,每退出一个元素,先访问该元素所指的结点
  3. 依次将该结点的左孩子结点(若存在的话)和右孩子结点(若存在的话)入队列。
  4. 如此重复下去,直到队列为空
#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. 求二叉树的深度(非递归算法)

算法思想:

  1. 对二叉树进行遍历,遍历过程中依次记录各个结点所处的层次数以及当前已经访问过的结点所处的最大层次数。
  2. 每当访问到某个叶子结点时,将该叶子结点所处的层次数与最大层次数进行比较
  3. 若前者大于后者,则修改最大层次数为该叶子结点的层次数,否则不作修改。
  4. 遍历结束时,所记录的最大层次数即为该二叉树的深度。

本算法使用的是非递归的中序遍历算法(其它遍历顺序也可以)

// 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. 采用后序遍历的非递归算法对二叉树进行遍历
  2. 遍历过程中对每一个结点判断其是否为满足条件的结点,若是满足条件的结点,则此时堆栈中保存的元素个数再加 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. 删除二叉树中以某个结点为根结点的子树

算法思想:

  1. 先序遍历找到符合条件的结点(其它遍历方法亦可)
  2. 然后删除以该结点为根结点的子树。
  3. 最后把该结点的父结点的相应的指针域置为 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. 插入排序

算法思想:

  1. 第 i 趟插入排序为:在含有 i − 1 个元素的有序子序列中插入一个元素,使之成为含有 i个元素的有序子序列。
  2. 在查找插入位置的过程中,可以同时后移元素。整个过程为进行 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. 快速排序

算法思路:

  1. 在参加排序的序列中任意选择一个元素(通常称为分界元素或基准元素),把小于或等于分界元素的所有元素都移到分界元素的前面,把大于分界元素的所有元素都移到分界元素的后面(这里使用挖坑法,考研过程中也是使用的这个方法)
  2. 这样,当前参加排序的序列就被划分成前后两个子序列,其中前一个子序列中的所有元素都小于后一个子序列的所有元素,并且分界元素正好处于排序的最终位置上。
  3. 然后分别对这两个子序列递归地进行上述排序过程,直到所有元素都处于排序的最终位置上,排序结束
#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;
}

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/829955.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【快应用】同时传递事件对象和自定义参数 ,无法正确接收事件对象

【关键词】 事件绑定、自定义参数、事件对象 【问题背景】 在快应用中采用如下方式绑定组件的事件方法&#xff0c;同时传递事件对象和自定义参数&#xff0c;发现回调方法中没有正确接收到事件对象。 问题代码如下&#xff1a; <template><!-- Only one root nod…

2023年信息系统项目管理师-粗略了解整体主要知识脉络

1. 五大过程组 2.十大知识领域 3. 十大知识领域与五大过程组的关系 4. 十大知识领域的关系图

Meta 烧掉百亿豪赌的未来:元宇宙,还有出路吗?

社交平台的下一步是什么&#xff1f; 2021年10月&#xff0c;Facebook联合创始人兼CEO马克扎克伯格在年度 Connect 开发者大会上正式宣布&#xff0c;Facebook公司将更名为Meta。这一举动标志着扎克伯格决心将公司转型为一家“元宇宙公司”&#xff0c;全力投入到元宇宙的技术…

【物联网】DMA传输原理与实现详解(超详细)

DMA&#xff08;Direct Memory Access&#xff0c;直接内存访问&#xff09;是一种计算机数据传输方式&#xff0c;允许外围设备直接访问系统内存&#xff0c;而无需CPU的干预。 文章目录 Part 1: DMA的工作原理配置阶段&#xff1a;数据传输阶段&#xff1a; Part 2: DMA数据…

java读写properties文件和xml文件,解决中文乱码问题

文章目录 前言一、properties文件1.1properties格式介绍1.2读取项目resource/templates下面properties并处理中文乱码问题1.3读取本地properties并处理中文乱码问题1.4修改properties文件 二、XML文件2.1xml文件格式2.2读取xml文件2.3写xml文件 前言 在开发当中我们经常需要用…

【NX】NX二次开发中拉伸曲线完整范例

之前做项目测试用到的代码&#xff0c;开发的版本是NX8.5的32位版本&#xff0c;这个代码实现起来也不难&#xff0c;其实就是调用了UG的API而已。 那么我在这里提供完整的代码&#xff1a; //author&#xff1a;autumoon //邮箱&#xff1a;9506163.com //日期&#xff1a;20…

spring AOP学习

概念 面向切面编程横向扩展动态代理 相关术语 动态代理 spring在运行期&#xff0c;生成动态代理对象&#xff0c;不需要特殊的编译器 Spring AOP的底层就是通过JDK动态代理或者CGLIb动态代理技术为目标Bean执行横向织入 目标对象实现了接口&#xff0c;spring使用JDK的ja…

利用微软Bing AI语言大模型辅助提高写代码、阅读代码、解bug的效率

目录 1 怎么才能使用Bing AI 2 Bing AI使用举例 2.1 代码没看懂&#xff0c;可以问Bing AI 2.2 当你不确定你程序理解的是否正确时&#xff0c;可以问Bing AI 2.3 程序编译出现bug&#xff0c;可以问Bing AI 1 怎么才能使用Bing AI 要想使用Bing AI&#xff0c;必须用微软…

【学会动态规划】剑指 Offer II 091. 粉刷房子(14)

目录 动态规划怎么学&#xff1f; 1. 题目解析 2. 算法原理 1. 状态表示 2. 状态转移方程 3. 初始化 4. 填表顺序 5. 返回值 3. 代码编写 写在最后&#xff1a; 动态规划怎么学&#xff1f; 学习一个算法没有捷径&#xff0c;更何况是学习动态规划&#xff0c; 跟我…

arm交叉编译lmbench

一、下载lmbench www.bitmover.com/lmbench 官网下载&#xff0c;http://www.bitmover.com/lmbench/lmbench3.tar.gz 我没有下载下来&#xff0c;找的别人的百度云下载 链接: https://pan.baidu.com/s/1tGo1clCqY-jQPN8G1eWSsg 提取码: f6jd 二、修改makefile 修改三个文件…

CrossOver2023虚拟机下载及如何安装软件教程

在当今数字化时代&#xff0c;虚拟机技术被广泛应用于软件开发、系统测试、网络安全等领域。虚拟机提供了一个隔离的虚拟环境&#xff0c;使得我们能够在一台物理计算机上同时运行多个操作系统和应用程序。下面我们就来看虚拟机软件怎么安装&#xff0c;虚拟机怎么使用吧&#…

浅析:单板静态数据分析功能在足底压力步态测量分析系统的应用

时代在不断进步&#xff0c;健康成为了现代人最重要的追求之一。而在我们繁忙的生活中&#xff0c;您是否关注过自己的足部健康&#xff1f;它不仅影响着我们的行走姿态、脊椎健康&#xff0c;还影响着我们的整体健康状态。 足底压力测量分析系统A30是一项采集人体足底压力的装…

【并发专题】单例模式的线程安全(进阶理解篇)

目录 背景前置知识类加载运行全过程 单例模式的实现方式一、饿汉式基本介绍源码分析 二、懒汉式基本介绍源码分析改进 三、懒汉式单例终极解决方案&#xff08;静态内部类&#xff09;&#xff08;推荐使用方案&#xff09;基本介绍源码分析 感谢 背景 最近学习了JVM之后&…

【HMS Core】位置服务逆地理编码请求错误问题

【关键字】 HMS、位置服务、逆地理编码 【问题描述】 有开发者反馈在集成位置服务-逆地理编码时&#xff0c;出现了请求报错的问题。 后端请求逆地理编码 错误 { "returnCode": "010010", "returnDesc": "INVALID_REQUEST" } 【问…

【雕爷学编程】Arduino动手做(181)---Maixduino AI开发板9

37款传感器与执行器的提法&#xff0c;在网络上广泛流传&#xff0c;其实Arduino能够兼容的传感器模块肯定是不止这37种的。鉴于本人手头积累了一些传感器和执行器模块&#xff0c;依照实践出真知&#xff08;一定要动手做&#xff09;的理念&#xff0c;以学习和交流为目的&am…

rest-apiV2.0.0升级为simplest-api开源框架生态之simplest-jpa发布

什么是 simplest simplest 追求存粹简单和极致。 旨在为项目快速开发提供一系列的基础能力&#xff0c;方便用户根据项目需求快速进行功能拓展 不在去关心一些繁琐。重复工作&#xff0c;而是把重点聚焦到业务。 前言 程序 10 年。作为一个多年程序。深知每个项目和程序&a…

最令跨境电商信服的六个电子商务营销策略

自从人们开始适应“新常态”以来&#xff0c;电子商务销售额一直在快速增长。进入智能手机和平板电脑时代&#xff0c;在线购物有望成为未来几年每个品牌的关键增长引擎。 数字零售格局的持续转型&#xff0c;您不得不考虑放弃一些利润较低的实体店&#xff0c;并进军电子商务&…

为什么说数字时代的品牌就是体验?

​ 在数字时代&#xff0c;品牌通常被称为“体验”&#xff0c;因为消费者现在以比以往更加动态和身临其境的方式与品牌互动。传统上&#xff0c;品牌主要侧重于创建独特的视觉形象并通过广告和其他营销渠道传达一致的信息。尽管这些方面仍然很重要&#xff0c;但数字环境已经…

应用案例|基于高精度3D视觉引导压缩机抓取定位应用

Part.1 行业现状 3D机器视觉是一种新兴的人工智能技术&#xff0c;它在机器视觉和机器学习领域中发挥着重要的作用。在工业领域&#xff0c;3D视觉技术被广泛应用于引导工业机器人进行抓取和定位操作。使用显扬科技的技术可以实现识别和定位压缩机。 Part.2 如何识别和定位压缩…

appium自动爬取数据

爬取类容&#xff1a;推荐知识点中所有的题目 爬取方式&#xff1a;appium模拟操作获取前端数据 入门级简单实现&#xff0c;针对题目和答案是文字内容的没有提取出来 适用场景;数据不多&#xff0c;参数加密&#xff0c;反爬严格等场景 from appium import webdriver impor…