考研数据结构上机题【36个模块77道题】5万字帮助你学会考研算法【完结篇】

专注 效率 记忆
预习 笔记 复习 做题

欢迎观看我的博客,如有问题交流,欢迎评论区留言,一定尽快回复!(大家可以去看我的专栏,是所有文章的目录)
 
文章字体风格:
红色文字表示:重难点★✔
蓝色文字表示:思路以及想法★✔
 
如果大家觉得有帮助的话,感谢大家帮忙
点赞!收藏!转发!

本博客带大家一起学习,我们不图快,只求稳扎稳打。
由于我高三是在家自学的,经验教训告诉我,学习一定要长期积累,并且复习,所以我推出此系列。
只求每天坚持40分钟,一周学5天,复习2天
也就是一周学10道题
60天后我们就可以学完81道题,相信60天后,我们一定可以有扎实的代码基础!我们每天就40分钟,和我一起坚持下去吧!
qq群:878080619

【考研408-数据结构(笔试)】

  • 一、排序
    • 1. 成绩排序(清华大学考研机试题)
        • 考点:结构体排序
          • 在结构体中定义排序
          • 使用比较器定义排序
        • 注意问题:需要处理 值相等时 先后顺序
    • 2. 成绩排序2( 清华大学考研机试题 )
  • 二、进位制
    • 1. 进制转换(清华大学考研机试题)
        • 本题考点
          • 1. 大数除法
          • 2. 十进制转二进制 步骤
    • 2. 进制转换2(清华大学考研机试题)
        • 考点:M进制转N进制
          • 和10进制转2进制 不一样的就是:短除时的 进位一个是10,而另一个是a仅此而已
  • 三、链表
    • 1. 两个链表的第一个公共结点
    • 2. 筛选链表( 2015年全国硕士研究生招生考试 )
        • 考点:删除节点
          • 需要两个指针,一个前一个后删除(所以比较的时候,比较p->next->val比较好)
    • 3. 重排链表( 2019年全国硕士研究生招生考试 )
        • 考点:1. 链表反转;2. 链表合并
  • 四、日期问题
    • 1. 日期累加( 北京理工大学考研机试题 )
    • 2. 打印日期( 华中科技大学考研机试题 )
  • 五、表达式求值
    • 1. 表达式求值
  • 六、树的遍历( 递归 )
    • 1. 二叉树的带权路径长度
    • 2. 重建二叉树
        • 构建二叉树的两种情况
  • 七、二叉搜索树与表达式树
    • 1. 二叉排序树(中序遍历是有序的)
    • 2. 表达式树
  • 八、Huffman树
    • 1. 合并果子
    • 2. 荷马史诗
        • 考点:( 构建深度最小的哈夫曼树 )利用pair
        • 考点:( k进制哈夫曼树需要补零节点 )
  • 八、拓扑排序
    • 1. 有向图的拓扑序列
  • 九、最小生成树、最短路
    • 1. Prim算法求最小生成树(和dijk算法差不多)
    • 2. Dijkstra求最短路 I
    • 3. Floyd求最短路
    • 4. spfa求最短路
  • 十、哈希表
    • 1. 模拟散列表
        • 开散列方法(拉链法)
        • 开放寻址法代码
            • 本质:(最多存1e5个数)
    • 2. 未出现过的最小正整数( 2018年全国硕士研究生招生考试 )
  • 十一、KMP
  • 十二、排序
    • 1. 快速排序
    • 2. 整数集合划分
  • 十三、多路归并
    • 1. 三元组的最小距离(2020年全国硕士研究生招生考试)
  • 十四、摩尔投票法
    • 1. 数组中出现次数超过一半的数字
  • 十五、DFS
    • 1. 全排列( 北京大学考研机试题 )
    • 2. 八皇后( 北京大学考研机试题 )
  • 十六、模拟
    • 1. 反序输出( 清华大学考研机试题 )
        • 考点:reverse(s.begin(), s.end());
    • 2. 特殊乘法( 清华大学考研机试题 )
    • 3. 众数( 清华大学考研机试题 )
    • 4. 糖果分享游戏
  • 十七、递推
    • 1. 递推数列( 清华大学考研机试题 )
    • 2. 吃糖果( 北京大学考研机试题 )
  • 十八、BFS
    • 1. 玛雅人的密码
    • 2. 等差数列
  • 十九、字符串处理
    • 1. 首字母大写
    • 2. 日志排序
    • 3. 字符串转换整数
  • 二十、递归,画图
    • 1. 重复者
  • 二十一、背包问题
    • 1. 01背包
    • 2. 神奇的口袋( 体积恰好是 )
    • 3. 整数拆分
  • 二十二、高精度
    • 1. N的阶乘
      • 做法:预处理
    • 2. 基本算术
    • 3. 整数查询
  • 二十三、因式分解
    • 1. 质因数的个数
    • 2. 约数个数
    • 3. 阶乘的末尾0(上海交通大学考研机试题)
    • 4. 整除问题(上海交通大学考研机试题)
  • 二十四、枚举
    • 1. 与7无关的数 (北京大学考研机试题)
    • 2. 打印极值点下标( 北京大学考研机试题 )
    • 3. 最简真分数( 北京大学考研机试题 ) 【真分数】
    • 4. 买房子 ( 北京大学考研机试题 )【算立方和平方】
    • 5. Special数(北京邮电大学考研机试题)
  • 二十六、位运算
    • 1. 位操作练习(北京大学考研机试题)
    • 2. 二进制数(北京邮电大学考研机试题)
  • 二十七、矩阵
    • 1. 旋转矩阵(北京航空航天大学考研机试题)
    • 2. 矩阵幂【矩阵相乘】(北京邮电大学考研机试题)
        • 两个矩阵相乘结果
    • 3. C翻转(北京邮电大学考研机试题)
  • 二十八、计算几何
    • 1. 球的计算
    • 2. 点的距离
        • 重新定义类的相减
    • 3. 直角三角形
  • 二十九、前缀和
    • 1. 最长平衡串(北京邮电大学考研机试题)
  • 三十、推公式
    • 1. 数字台阶
    • 2. 整数和
    • 3. 弹地小球
  • 三十一、最短路
    • 1. 我想回家(北京大学考研机试题)
    • 2. 最短路径
    • 3. 最短路径
  • 三十二、思维题
    • 1. 棋盘遍历问题(上海交通大学考研机试题)
  • 三十三、哈希表
    • 1. 子串计算(北京大学考研机试题)
        • map自动排序
    • 2. 查找(北京邮电大学考研机试题)
        • unordered_set(通过count(x)==0判断是否存在)
    • 3. 单词识别(北京理工大学考研机试题)
  • 三十四、双指针
    • 1. 最小面积子矩阵
        • 算法1:二维前缀和
        • 算法2:一维前缀和 + 双指针
  • 三十五、序列型DP
    • 1. 最大序列和(清华大学考研机试题)
    • 2. 最长ZigZag子序列
  • 三十六、红黑树和并查集
    • 1. 合并集合

一、排序

1. 成绩排序(清华大学考研机试题)

考研数据结构上机题【36个模块77道题】5万字帮助你学会考研算法【完结篇】_第1张图片

考点:结构体排序

在结构体中定义排序
#include 
#include 
#include 

using namespace std;

const int N = 1010;

int n, m;
struct Person
{
    string name;
    int score;
    int id;

    bool operator< (const Person& t) const
    {
        if (score != t.score) return score < t.score;
        return id < t.id;
    }
    bool operator> (const Person& t) const
    {
        if (score != t.score) return score > t.score;
        return id < t.id;
    }
}q[N];

int main()
{
    cin >> n >> m;
    for (int i = 0; i < n; i ++ )
    {
        cin >> q[i].name >> q[i].score;
        q[i].id = i;
    }

    if (!m) sort(q, q + n, greater<Person>());
    else sort(q, q + n);

    for (int i = 0; i < n; i ++ )
        cout << q[i].name << ' ' << q[i].score << endl;
    return 0;
}
使用比较器定义排序
#include
using namespace std;
struct node{
    int x,id;
    string name;
}a[1001];
bool cmp1(node a,node b){
//从小到大
    if(a.x==b.x)return a.id<b.id;
    return a.x<b.x;
}
bool cmp2(node a,node b){
//从大到小
    if(a.x==b.x)return a.id<b.id;
    return a.x>b.x;
}
int main()
{
    int n;
    bool ok;
    cin>>n>>ok;
    for(int i=1;i<=n;i++){
        cin>>a[i].name>>a[i].x;
        a[i].id=i;
    }
    if(ok)sort(a+1,a+1+n,cmp1);
    if(!ok)sort(a+1,a+1+n,cmp2);
    for(int i=1;i<=n;i++)
        cout<<a[i].name<<' '<<a[i].x<<'\n';
}

注意问题:需要处理 值相等时 先后顺序

在结构体中定义一个id,记录出现的顺序

// package Test;

import java.lang.reflect.Array;
import java.util.Arrays;
import java.util.Scanner;

/**
 * @Author zh
 * @Date 2023/8/8 19:10
 * @PackageName:Test
 * @ClassName: Main
 * @Description: TODO
 * @Version 1.0
 */

class Student{
    int grade;
    String name;
    public Student(int grade,String name){
        this.grade = grade;
        this.name = name;
    }
}

class Main {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        int N = scanner.nextInt();
        int a = scanner.nextInt();
        Student[] students = new Student[N];
        for(int i = 0; i < N; i++){
            String name = scanner.next();
            int grade = scanner.nextInt();
            students[i] = new Student(grade,name);
        }

         if(a == 1){
             Arrays.sort(students,(o1, o2) -> {
                 return o1.grade-o2.grade;
             });
         }else{
             Arrays.sort(students,(o1, o2) -> {
                 return o2.grade-o1.grade;
             });
         }

        for(Student x : students){
            System.out.println(x.name + " " + x.grade);
        }


    }
}

2. 成绩排序2( 清华大学考研机试题 )

考研数据结构上机题【36个模块77道题】5万字帮助你学会考研算法【完结篇】_第2张图片

#include 
#include 
#include 

using namespace std;

const int N = 110;

int n;
struct Person
{
    int id;
    int score;
    bool operator< (const Person& t) const
    {
        if (score != t.score) return score < t.score;
        return id < t.id;
    }
}q[N];

int main()
{
    cin >> n;
    for (int i = 0; i < n; i ++ ) cin >> q[i].id >> q[i].score;
    sort(q, q + n);

    for (int i = 0; i < n; i ++ ) cout << q[i].id << ' ' << q[i].score << endl;
    return 0;
}
import java.util.Arrays;
import java.util.Scanner;

class Student{
    int id;
    int grade;
    public Student(int id,int grade){
        this.id = id;
        this.grade = grade;
    }
}

class Main{
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        int n = scanner.nextInt();
        Student[] students = new Student[n];
        for(int i = 0; i < n; i++){
            int id = scanner.nextInt();
            int grade = scanner.nextInt();
            students[i] = new Student(id,grade);
        }
        Arrays.sort(students,(o1, o2) -> {
            if(o1.grade == o2.grade){
                return o1.id-o2.id;
            }
            return o1.grade-o2.grade;
        });
        for (Student x : students){
            System.out.println(x.id + " " + x.grade);
        }
    }
}

二、进位制

1. 进制转换(清华大学考研机试题)

考研数据结构上机题【36个模块77道题】5万字帮助你学会考研算法【完结篇】_第3张图片

本题考点

1. 大数除法

可以csdn搜索一下

关键就是vector的使用 和 除法步骤记得就好

就好比用 代码模拟出 草稿纸上的除法运算

2. 十进制转二进制 步骤

考研数据结构上机题【36个模块77道题】5万字帮助你学会考研算法【完结篇】_第4张图片

代码看不懂可以,只需明白逻辑,然后自己写即可

一定自己写出来,才明白

#include
#include
#include
#include

using namespace std;

void div(vector<int>& tmp,vector<int>& ans)
{
    vector<int> tmp2;
    int t = 0;
    for(int i = 0; i < tmp.size(); i++)
    {
        t = t*10 + tmp[i];
        tmp2.push_back(t/2);
        t = t%2;
    }
    ans.push_back(t);
    reverse(tmp2.begin(),tmp2.end());
    while(tmp2.size()!=1 && tmp2.back()==0)
    {
        tmp2.pop_back();
    }
    reverse(tmp2.begin(),tmp2.end());
    tmp = tmp2;
}

int main()
{
    string s;
    while(cin >> s)
    {
        vector<int> tmp;
        
        for(int i = 0; i < s.size(); i++)
        {
            tmp.push_back(s[i]-'0');
        }
        
        vector<int> ans;    
        //什么时候停止
        if(s=="0")
        {
            cout << 0 << endl;
        }
        else
        {
            while(tmp.size()>1 || (tmp.size() == 1 && tmp.back() != 0))
            {
                div(tmp,ans);
            }
            reverse(ans.begin(),ans.end());
            for(int i = 0; i < ans.size(); i++)
            {
                cout << ans[i];
            }
            cout << endl;
        }
    }
    return 0;
}

2. 进制转换2(清华大学考研机试题)

考研数据结构上机题【36个模块77道题】5万字帮助你学会考研算法【完结篇】_第5张图片

考点:M进制转N进制

和10进制转2进制 不一样的就是:短除时的 进位一个是10,而另一个是a仅此而已
#include 
#include 
#include 
#include 

using namespace std;

int main()
{
    int a, b;
    string s;
    cin >> a >> b >> s;
    vector<int> A;
    for (int i = 0; i < s.size(); i ++ )
    {
        char c = s[s.size() - 1 - i];
        if (c >= 'A') A.push_back(c - 'A' + 10);
        else A.push_back(c - '0');
    }
    string res;
    if (s == "0") res = "0";
    else
    {
        while (A.size())
        {
            int r = 0;
            for (int i = A.size() - 1; i >= 0; i -- )
            {
                A[i] += r * a;
                r = A[i] % b;
                A[i] /= b;
            }
            while (A.size() && A.back() == 0) A.pop_back();
            if (r < 10) res += to_string(r);
            else res += r - 10 + 'a';
        }
        reverse(res.begin(), res.end());
    }
    cout << res << endl;

    return 0;
}

三、链表

1. 两个链表的第一个公共结点

在这里插入图片描述
考研数据结构上机题【36个模块77道题】5万字帮助你学会考研算法【完结篇】_第6张图片

//cpp
class Solution {
public:
    ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) {
        ListNode *p1 = headA;
        ListNode *p2 = headB;

        while (p1 != p2) {
            if(p1 != NULL)//p1没有走到结尾
                p1 = p1->next;//p1指向下一个节点
            else//p1走到结尾
                p1 = headB;//p1指向另一个链表头
            if(p2 != NULL)//p2没有走到结尾
                p2 = p2->next;//p2指向下一个节点
            else  //p2走到结尾 
                p2 = headA;//p2指向另一个链表头
        }
        return p1;
    }
};

2. 筛选链表( 2015年全国硕士研究生招生考试 )

考点:删除节点

需要两个指针,一个前一个后删除(所以比较的时候,比较p->next->val比较好)
/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
class Solution {
public:
    ListNode* filterList(ListNode* head) {
        bool st[10001] = {};
        st[abs(head->val)] = true;
        for (auto p = head; p->next;) {
            int x = abs(p->next->val);
            if (st[x]) {
                auto q = p->next;
                p->next = q->next;
                delete q;
            } else {
                p = p->next;
                st[x] = true;
            }
        }
        return head;
    }
};

3. 重排链表( 2019年全国硕士研究生招生考试 )

考研数据结构上机题【36个模块77道题】5万字帮助你学会考研算法【完结篇】_第7张图片

考点:1. 链表反转;2. 链表合并

考研数据结构上机题【36个模块77道题】5万字帮助你学会考研算法【完结篇】_第8张图片

class Solution {
/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
public:
    void rearrangedList(ListNode* head) {
        // 若只有一个元素,则不需要操作
        if (!head->next) {
            return;
        }
        // 计算链表长度
        int len = 0;
        for (auto p = head; p; p = p->next) {
            len++;
        }
        // 移动到链表中间处
        int mid = (len + 1) / 2;
        auto a = head;
        for (int i = 0; i < mid - 1; i++) {
            // 由于该链表没有设头结点,head 就是具有值的结点,因此到 mid - 1
            a = a->next;
        }

        // 反转后半段链表,b在前,c在后
        auto b = a->next, c = b->next;
        // a->next 是为了从中间将链表截断;b->next 是因为此时的 b 是反转后链表的结尾元素
        a->next = b->next = NULL;
        while (c) {
            auto temp = c->next;
            c->next = b;
            b = c;
            c = temp;
        }

        // 合并链表,注意此时 b 指向反转链表头部,c 指向 NULL
        for (auto p = head, q = b; q;) {
            auto qq = q->next;
            // 插入结点
            q->next = p->next;
            p->next = q;
            // 移动p和q
            p = q->next;
            q = qq;            
        }
    }
};

四、日期问题

1. 日期累加( 北京理工大学考研机试题 )

考研数据结构上机题【36个模块77道题】5万字帮助你学会考研算法【完结篇】_第9张图片

#include 
using namespace std;

//平年每月的天数
const int months[] = {
  0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
};

//判断是否是闰年
bool is_leap(int y)
{
    if(y % 400 == 0 || (y % 100 && y % 4 == 0)) return true;
    return false;
}

//获得y年第m月的天数
int get_days(int y, int m)
{
    if(m == 2) return months[m] + is_leap(y);
    return months[m];
}

//获得y年第m月--y + 1年第m月的的天数
int get_year_days(int y, int m)
{
    if(m <= 2) return (365 + is_leap(y));
    return (365 + is_leap(y + 1));
}

int main()
{
    int t;
    cin >> t;

    int y, m, d, days;

    while(t -- )
    {
        cin >> y >> m >> d >> days;

        if(m == 2 && d == 29)
        {
            days --;
            m = 3, d = 1;
        }

        while(days > get_year_days(y, m))
        {
            days -= get_year_days(y, m);
            y ++;
        }

        while(days --)
        {
            d ++;
            if(d > get_days(y, m))
            {
                m ++, d = 1;
                if(m > 12) y ++, m = 1;
            }
        }
        printf("%04d-%02d-%02d\n", y, m, d);

    }
    return 0;
}

2. 打印日期( 华中科技大学考研机试题 )

考研数据结构上机题【36个模块77道题】5万字帮助你学会考研算法【完结篇】_第10张图片

#include 
#include 
#include 

using namespace std;

const int months[13] = {
    0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
};

int is_leap(int year)  // 闰年返回1,平年返回0
{
    if (year % 4 == 0 && year % 100 || year % 400 == 0)
        return 1;
    return 0;
}

int get_days(int y, int m)  // y年m月有多少天
{
    if (m == 2) return months[m] + is_leap(y);
    return months[m];
}

int main()
{
    int y, s;

    while (cin >> y >> s)
    {
        int m = 1, d = 1;
        s -- ;
        while (s -- )
        {
            if ( ++ d > get_days(y, m))
            {
                d = 1;
                if ( ++ m > 12)
                {
                    m = 1;
                    y ++ ;
                }
            }
        }
        printf("%04d-%02d-%02d\n", y, m, d);
    }
    return 0;
}

五、表达式求值

1. 表达式求值

考研数据结构上机题【36个模块77道题】5万字帮助你学会考研算法【完结篇】_第11张图片

考研数据结构上机题【36个模块77道题】5万字帮助你学会考研算法【完结篇】_第12张图片
考研数据结构上机题【36个模块77道题】5万字帮助你学会考研算法【完结篇】_第13张图片

#include 
#include 
#include 
#include 
using namespace std;

stack<int> num;
stack<char> op;

//优先级表
unordered_map<char, int> h{ {'+', 1}, {'-', 1}, {'*',2}, {'/', 2} };


void eval()//求值
{
    int a = num.top();//第二个操作数
    num.pop();

    int b = num.top();//第一个操作数
    num.pop();

    char p = op.top();//运算符
    op.pop();

    int r = 0;//结果 

    //计算结果
    if (p == '+') r = b + a;
    if (p == '-') r = b - a;
    if (p == '*') r = b * a;
    if (p == '/') r = b / a;

    num.push(r);//结果入栈
}

int main()
{
    string s;//读入表达式
    cin >> s;

    for (int i = 0; i < s.size(); i++)
    {
        if (isdigit(s[i]))//数字入栈
        {
            int x = 0, j = i;//计算数字
            while (j < s.size() && isdigit(s[j]))
            {
                x = x * 10 + s[j] - '0';
                j++;
            }
            num.push(x);//数字入栈
            i = j - 1;
        }
        //左括号无优先级,直接入栈
        else if (s[i] == '(')//左括号入栈
        {
            op.push(s[i]);
        }
        //括号特殊,遇到左括号直接入栈,遇到右括号计算括号里面的
        else if (s[i] == ')')//右括号
        {
            while(op.top() != '(')//一直计算到左括号
                eval();
            op.pop();//左括号出栈
        }
        else
        {
            while (op.size() && h[op.top()] >= h[s[i]])//待入栈运算符优先级低,则先计算
                eval();
            op.push(s[i]);//操作符入栈
        }
    }
    while (op.size()) eval();//剩余的进行计算
    cout << num.top() << endl;//输出结果
    return 0;
}

六、树的遍历( 递归 )

1. 二叉树的带权路径长度

考研数据结构上机题【36个模块77道题】5万字帮助你学会考研算法【完结篇】_第14张图片

class Solution {
public:
    int sum(TreeNode* root, int level) {
        if(!root) return 0;
        if(!root->left && !root->right) {
            return root->val * level;
        }
        return sum(root->left, level + 1) + sum(root->right, level + 1);
    }

    int pathSum(TreeNode* root) {
        return sum(root, 0);
    }
};

2. 重建二叉树

构建二叉树的两种情况

构建二叉树的两种情况【根据前序遍历和中序遍历 构造树】【根据后序遍历和中序遍历 构造树】
考研数据结构上机题【36个模块77道题】5万字帮助你学会考研算法【完结篇】_第15张图片

/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode(int x) : val(x), left(NULL), right(NULL) {}
 * };
 */
class Solution {
public:

    unordered_map<int,int> pos;

    TreeNode* buildTree(vector<int>& preorder, vector<int>& inorder) {
        int n = preorder.size();
        for (int i = 0; i < n; i ++ )
            pos[inorder[i]] = i;
        return dfs(preorder, inorder, 0, n - 1, 0, n - 1);
    }

    TreeNode* dfs(vector<int>&pre, vector<int>&in, int pl, int pr, int il, int ir)
    {
        if (pl > pr) return NULL;
        int k = pos[pre[pl]] - il;
        TreeNode* root = new TreeNode(pre[pl]);
        root->left = dfs(pre, in, pl + 1, pl + k, il, il + k - 1);
        root->right = dfs(pre, in, pl + k + 1, pr, il + k + 1, ir);
        return root;
    }
};

七、二叉搜索树与表达式树

1. 二叉排序树(中序遍历是有序的)

考研数据结构上机题【36个模块77道题】5万字帮助你学会考研算法【完结篇】_第16张图片
考研数据结构上机题【36个模块77道题】5万字帮助你学会考研算法【完结篇】_第17张图片
考研数据结构上机题【36个模块77道题】5万字帮助你学会考研算法【完结篇】_第18张图片
考研数据结构上机题【36个模块77道题】5万字帮助你学会考研算法【完结篇】_第19张图片
解释一下删除操作的第三点:

首先我们要清楚:
二叉排序树的特点是
中序遍历(左根右)是有序的

所以如果删除的根节点有左右子树

那么我们为了保证有序

就要把 根节点左子树的最右边 赋值给根节点
因为根节点左子树的最右边是比根节点小中最大的点

只有这样才能保证删除根节点后

左子树比新的根节点都小
右子树比新的根节点都大

#include 

using namespace std;

const int INF = 2e9;

struct TreeNode
{
    int val;
    TreeNode *left, *right;
    TreeNode(int _val): val(_val), left(NULL), right(NULL) {}
}*root;

// 插入操作
void insert(TreeNode* &root, int x)
{
    /*
        1. 递归找到x的待插入的位置
        2. 如果x < 当前root就递归到左子树,反之,递归到右子树。
    */ 
    if (!root) root = new TreeNode(x);
    // 如果发现数值相同的话就判断一下;
    if (root->val == x) return;
    else if (x < root->val) insert(root->left, x);
    else insert(root->right, x);
}

void remove(TreeNode* &root, int x)
{
    /*
        1. 待删除的结点只有左子树。立即可推出,左子树上的结点都是小于待删除的结点的,我们只需要把待删除结点删了然后左子树接上待删除结点的父节点就可以了。
        2. 待删除的结点只有右子树。立即可推出,右子树上的结点都是大于待删除的结点的,我们只需要把待删除结点删了然后右子树接上待删除结点的父节点就可以了。
        3. 待删除的结点既有左子树又有右子树。这个比上两个情况麻烦一点,但也不麻烦,需要读者理解的是,怎么能删除结点后还不改变中序遍历的结果,并且操作代价最小,显而易见,我们根据待删除结点的左子树可以得到最右下角的最后结点满足$

    // 如果不存在直接return
    if (!root) return;
    if (x < root->val) remove(root->left, x);
    else if (x > root->val) remove(root->right, x);
    else
    {
        if (!root->left && !root->right) root = NULL;
        else if (!root->left) root = root->right;
        else if (!root->right) root = root->left;
        else
        {
            auto p = root->left;
            while (p->right) p = p->right;
            root->val = p->val;
            remove(root->left, p->val);
        }
    }
}

// 输出数值 x 的前驱(前驱定义为现有所有数中小于 x 的最大的数)。
int get_pre(TreeNode* root, int x)
{
    if (!root) return -INF;
    if (root->val >= x) return get_pre(root->left, x);
    else return max(root->val, get_pre(root->right, x));
}

// 输出数值 x 的后继(后继定义为现有所有数中大于 x 的最小的数)。
int get_suc(TreeNode* root, int x)
{
    if (!root) return INF;
    if (root->val <= x) return get_suc(root->right, x);
    else return min(root->val, get_suc(root->left, x));
}

int main()
{
    int n;
    cin >> n;
    while (n--)
    {
        int t, x;
        cin >> t >> x;
        if (t == 1) insert(root, x);
        else if (t == 2) remove(root, x);
        else if (t == 3) cout << get_pre(root, x) << endl;
        else cout << get_suc(root, x) << endl;
    }
    return 0;
}

2. 表达式树

本题就是中序遍历
只要不是叶子节点
就加左括号然后遍历左边
遍历完右边加右括号

考研数据结构上机题【36个模块77道题】5万字帮助你学会考研算法【完结篇】_第20张图片

class Solution {
public:
    string dfs(TreeNode* root) {
        if(!root) return "";
        if(!root->left && !root->right) {
            return root->val;
        }
        string ret = "";
        ret += '(';
        ret += dfs(root->left);
        ret += root->val;
        ret += dfs(root->right);
        ret += ')';
        return ret;
    }

    string expressionTree(TreeNode* root) {
        return dfs(root->left) + root->val + dfs(root->right);
    }
};

八、Huffman树

1. 合并果子

考研数据结构上机题【36个模块77道题】5万字帮助你学会考研算法【完结篇】_第21张图片
什么是哈夫曼树

本题就记住哈夫曼树的最简代码如下:

#include 
#include 
#include 

using namespace std;

int main()
{
    int n;
    scanf("%d", &n);

    priority_queue<int, vector<int>, greater<int>> heap;
    while (n -- )
    {
        int x;
        scanf("%d", &x);
        heap.push(x);
    }

    int res = 0;
    while (heap.size() > 1)
    {
        int a = heap.top(); heap.pop();
        int b = heap.top(); heap.pop();
        res += a + b;
        heap.push(a + b);
    }

    printf("%d\n", res);
    return 0;
}

2. 荷马史诗

考点:( 构建深度最小的哈夫曼树 )利用pair

可以忽略这个段,下面有
图中所说的合并的次数,本质其实是根节点的深度
所以优先队列的元素是pair
一个记录权值 一个记录深度

考点:( k进制哈夫曼树需要补零节点 )

题解图中有解释
原题链接

考研数据结构上机题【36个模块77道题】5万字帮助你学会考研算法【完结篇】_第22张图片
考研数据结构上机题【36个模块77道题】5万字帮助你学会考研算法【完结篇】_第23张图片
图中所说的合并的次数,本质其实是根节点的深度

所以优先队列的元素是pair
一个记录权值 一个记录深度

#include 
#include 
#include 
#include 

#define x first
#define y second

using namespace std;

typedef long long LL;
typedef pair<LL, int> PLI;

int main()
{
    int n, k;
    scanf("%d%d", &n, &k);
    priority_queue<PLI, vector<PLI>, greater<PLI>> heap;
    while (n -- )
    {
        LL w;
        scanf("%lld", &w);
        heap.push({w, 0});
    }
    while ((heap.size() - 1) % (k - 1)) heap.push({0, 0});

    LL res = 0;
    while (heap.size() > 1)
    {
        LL s = 0;
        int depth = 0;
        for (int i = 0; i < k; i ++ )
        {
            auto t = heap.top();
            heap.pop();
            s += t.x, depth = max(depth, t.y);
        }
        heap.push({s, depth + 1});
        res += s;
    }

    printf("%lld\n%d\n", res, heap.top().y);
    return 0;
}

八、拓扑排序

1. 有向图的拓扑序列

原题链接
在这里插入图片描述

这就是一个模板
算法原理可以csdn搜一下

#include 
#include 
#include 

using namespace std;

const int N = 100010;

int n, m;
int h[N], e[N], ne[N], idx;
int d[N];
int q[N];

void add(int a, int b)
{
    e[idx] = b, ne[idx] = h[a], h[a] = idx ++ ;
}

bool topsort()
{
    int hh = 0, tt = -1;

    for (int i = 1; i <= n; i ++ )
        if (!d[i])
            q[ ++ tt] = i;

    while (hh <= tt)
    {
        int t = q[hh ++ ];

        for (int i = h[t]; i != -1; i = ne[i])
        {
            int j = e[i];
            if (-- d[j] == 0)
                q[ ++ tt] = j;
        }
    }

    return tt == n - 1;
}

int main()
{
    scanf("%d%d", &n, &m);

    memset(h, -1, sizeof h);

    for (int i = 0; i < m; i ++ )
    {
        int a, b;
        scanf("%d%d", &a, &b);
        add(a, b);

        d[b] ++ ;
    }

    if (!topsort()) puts("-1");
    else
    {
        for (int i = 0; i < n; i ++ ) printf("%d ", q[i]);
        puts("");
    }

    return 0;
}

九、最小生成树、最短路

1. Prim算法求最小生成树(和dijk算法差不多)

原题链接

在这里插入图片描述
从1节点出发,每次走最短路径(距离集合的最短路径用d表示)选出最短路径再加到res上
(prim算法和dijkstra算法差不多,只是d的表示含义不同)

#include 
#include 
#include 

using namespace std;

const int N = 510, M = 100010, INF = 0x3f3f3f3f;

int n, m;
int g[N][N], dist[N];
bool st[N];

int prim()
{
    memset(dist, 0x3f, sizeof dist);
    dist[1] = 0;
    int res = 0;
    for (int i = 0; i < n; i ++ )
    {
        int t = -1;
        for (int j = 1; j <= n; j ++ )
            if (!st[j] && (t == -1 || dist[t] > dist[j]))
                 t = j;
        if (dist[t] == INF) return INF;
        st[t] = true;
        res += dist[t];
        for (int j = 1; j <= n; j ++ )
            dist[j] = min(dist[j], g[t][j]);
    }
    return res;
}

int main()
{
    scanf("%d%d", &n, &m);
    memset(g, 0x3f, sizeof g);
    while (m -- )
    {
        int a, b, c;
        scanf("%d%d%d", &a, &b, &c);
        g[a][b] = g[b][a] = min(g[a][b], c);
    }

    int res = prim();
    if (res == INF) puts("impossible");
    else printf("%d\n", res);
    return 0;
}

2. Dijkstra求最短路 I

在这里插入图片描述

#include 
#include 
#include 

using namespace std;

const int N = 1e5 + 10;

int n, m;
int head[N], e[N], ne[N], w[N], idx;
bool st[N];
int dist[N];

void add(int a, int b, int c)
{
    e[idx] = b;
    w[idx] = c;
    ne[idx] = head[a];
    head[a] = idx++;
}

int spfa()
{
    memset(dist, 0x3f, sizeof dist);
    dist[1] = 0;

    queue<int> q;
    q.push(1);

    st[1] = true;  //判重数组, 队列中有重复的点没有意义

    while (q.size()) {
        int t = q.front();
        q.pop();

        st[t] = false;

        for (int i = head[t]; i != -1; i = ne[i]) {
            int j = e[i];
            if (dist[j] > dist[t] + w[i]) {
                dist[j] = dist[t] + w[i];
                if (!st[j]) {
                    q.push(j);
                    st[j] = true;
                }
            }
        }
    }
    if (dist[n] == 0x3f3f3f3f) {
        return -1;
    }
    return dist[n];
}

int main()
{
    cin >> n >> m;

    memset(head, -1, sizeof head);

    for (int i = 0; i < m; i++) {
        int a, b, c;
        cin >> a >> b >> c;
        add(a, b, c);
    }

    int t = spfa();

    if (t == -1) {
        cout << -1 << endl;
    }
    else {
        cout << dist[n] << endl;
    }

    return 0;
}

3. Floyd求最短路

在这里插入图片描述

#include 
#include 
#include 

using namespace std;

const int N = 210, INF = 0x3f3f3f3f;

int n, m, Q;
int d[N][N];

int main()
{
    scanf("%d%d%d", &n, &m, &Q);
    memset(d, 0x3f, sizeof d);
    for (int i = 1; i <= n; i ++ ) d[i][i] = 0;

    while (m -- )
    {
        int a, b, c;
        scanf("%d%d%d", &a, &b, &c);
        d[a][b] = min(d[a][b], c);
    }

    for (int k = 1; k <= n; k ++ )
        for (int i = 1; i <= n; i ++ )
            for (int j = 1; j <= n; j ++ )
                d[i][j] = min(d[i][j], d[i][k] + d[k][j]);

    while (Q -- )
    {
        int a, b;
        scanf("%d%d", &a, &b);
        int c = d[a][b];
        if (c > INF / 2) puts("impossible");
        else printf("%d\n", c);
    }

    return 0;
}

4. spfa求最短路

权值可能为负
所以需要每条路径都走
而不是像dijkstra算法只走一部分

所以spfa算法用普通队列存储即可
并且每个点可能走多次,所以st需要再次false
在这里插入图片描述

#include 
#include 
#include 
#include 

using namespace std;

const int N = 100010;

int n, m;
int h[N], w[N], e[N], ne[N], idx;
int dist[N];
bool st[N];

void add(int a, int b, int c)
{
    e[idx] = b, w[idx] = c, ne[idx] = h[a], h[a] = idx ++ ;
}

int spfa()
{
    memset(dist, 0x3f, sizeof dist);
    dist[1] = 0;

    queue<int> q;
    q.push(1);
    st[1] = true;

    while (q.size())
    {
        int t = q.front();
        q.pop();

        st[t] = false;

        for (int i = h[t]; i != -1; i = ne[i])
        {
            int j = e[i];
            if (dist[j] > dist[t] + w[i])
            {
                dist[j] = dist[t] + w[i];
                if (!st[j])
                {
                    q.push(j);
                    st[j] = true;
                }
            }
        }
    }

    return dist[n];
}

int main()
{
    scanf("%d%d", &n, &m);

    memset(h, -1, sizeof h);

    while (m -- )
    {
        int a, b, c;
        scanf("%d%d%d", &a, &b, &c);
        add(a, b, c);
    }

    int t = spfa();

    if (t == 0x3f3f3f3f) puts("impossible");
    else printf("%d\n", t);

    return 0;
}

十、哈希表

1. 模拟散列表

在这里插入图片描述

开散列方法(拉链法)

就记住有N个链表头节点

对于原数据可以 (x % N + N) % N;找到合适位置插入到头节点

#include 
#include 

using namespace std;

const int N = 1e5 + 3;  // 取大于1e5的第一个质数,取质数冲突的概率最小 可以百度

//* 开一个槽 h
int h[N], e[N], ne[N], idx;  //邻接表

void insert(int x) {
    // c++中如果是负数 那他取模也是负的 所以 加N 再 %N 就一定是一个正数
    int k = (x % N + N) % N;
    e[idx] = x;
    ne[idx] = h[k];
    h[k] = idx++;
}

bool find(int x) {
    //用上面同样的 Hash函数 讲x映射到 从 0-1e5 之间的数
    int k = (x % N + N) % N;
    for (int i = h[k]; i != -1; i = ne[i]) {
        if (e[i] == x) {
            return true;
        }
    }
    return false;
}

int n;

int main() {
    cin >> n;

    memset(h, -1, sizeof h);  //将槽先清空 空指针一般用 -1 来表示

    while (n--) {
        string op;
        int x;
        cin >> op >> x;
        if (op == "I") {
            insert(x);
        } else {
            if (find(x)) {
                puts("Yes");
            } else {
                puts("No");
            }
        }
    }
    return 0;
}

开放寻址法代码

本质:(最多存1e5个数)
#include 
#include 

using namespace std;

//开放寻址法一般开 数据范围的 2~3倍, 这样大概率就没有冲突了
const int N = 2e5 + 3;        //大于数据范围的第一个质数
const int null = 0x3f3f3f3f;  //规定空指针为 null 0x3f3f3f3f

int h[N];

int find(int x) {
    int t = (x % N + N) % N;
    while (h[t] != null && h[t] != x) {
        t++;
        if (t == N) {
            t = 0;
        }
    }
    return t;  //如果这个位置是空的, 则返回的是他应该存储的位置
}

int n;

int main() {
    cin >> n;

    memset(h, 0x3f, sizeof h);  //规定空指针为 0x3f3f3f3f

    while (n--) {
        string op;
        int x;
        cin >> op >> x;
        if (op == "I") {
            h[find(x)] = x;
        } else {
            if (h[find(x)] == null) {
                puts("No");
            } else {
                puts("Yes");
            }
        }
    }
    return 0;
}

2. 未出现过的最小正整数( 2018年全国硕士研究生招生考试 )

在这里插入图片描述
由于我们需要从1去找 是否出现在数组中

如果1去遍历一遍数组
2遍历一遍数组
太麻烦

如何一步到位?
其实可以用
哈希思想

把数组出现的数都映射存储到数组中

如何都没有出现

那么一定是大于数组的个数+1的那个值

class Solution {
public:
    int findMissMin(vector<int>& nums) {
        int n = nums.size();
        vector<bool> hash(n + 1);
        for (int x: nums)
            if (x >= 1 && x <= n)
                hash[x] = true;
        for (int i = 1; i <= n; i ++ )
            if (!hash[i])
                return i;
        return n + 1;
    }
};

十一、KMP

在这里插入图片描述

KMP是快速帮助 子串A 去匹配 主串B的算法

我们利用next记录
当B和A不匹配的时候,A应该返回B中的哪个位置

所以next应该记录的是B的快速匹配位置

具体逻辑可以搜CSDN

#include 
#include 
#include 

using namespace std;

const int N = 100010, M = 1000010;

int n, m;
char p[N], s[M];
int ne[N];

int main()
{
    scanf("%d%s", &n, p + 1);
    scanf("%d%s", &m, s + 1);

    for (int i = 2, j = 0; i <= n; i ++ )
    {
        while (j && p[i] != p[j + 1]) j = ne[j];
        if (p[i] == p[j + 1]) j ++ ;
        ne[i] = j;
    }

    for (int i = 1, j = 0; i <= m; i ++ )
    {
        while (j && s[i] != p[j + 1]) j = ne[j];
        if (s[i] == p[j + 1]) j ++ ;
        if (j == n) printf("%d ", i - n);
    }
    return 0;
}

十二、排序

1. 快速排序

在这里插入图片描述

  1. 快速排序的核心思想是:
    大于x的 放到数组右边
    小于x的放在数组左边
    等于x的放在数组中间
  2. 剩下的背模板即可
#include
#include
#include
using namespace std;
const int N=100020;
int a[N];
int n;
void quick_sort(int l,int r)
{
    if(l>=r) return ;
    int k=a[l+r>>1],i=l-1,j=r+1;
    while(i<j)
    {
        do i++; while(a[i]<k);
        do j--; while(a[j]>k);
        if(i<j) swap(a[i],a[j]);
    }
    quick_sort(l,j);
    quick_sort(j+1,r);
}
int main()
{
    scanf("%d",&n);
    for(int i=0;i<n;i++)
    scanf("%d",&a[i]);
    quick_sort(0,n-1);
    for(int i=0;i<n;i++)
    printf("%d ",a[i]);
    return 0;
}

2. 整数集合划分

在这里插入图片描述

#include 
#include 
using namespace std;

const int N = 100010;
int a[N];

int sum(int a[], int l, int r)//求数组 a[l] ~ a[r] 的和
{ 
    int res = 0;
    for(int i = l; i <= r; i++)
        res += a[i];
    return res;
}

int main()
{
    int n;
    cin >> n;
    for(int i = 0; i < n; i++)
    {
        cin >> a[i];
    }
    sort(a,a + n);//排序

    int s1, s2;

    s1 = sum(a, 0, n/2-1);//s1 的和小
    s2 = sum(a, n/2, n - 1);//s2 的和大

    cout << n % 2 << " " << s2 - s1;
}

十三、多路归并

1. 三元组的最小距离(2020年全国硕士研究生招生考试)

在这里插入图片描述
在这里插入图片描述
什么是多路归并?
就是三个数组排序到一个数组进行比较
是需要开一个数组的空间的

为了节约空间,我们可以通过三个数组分别用三个指针
指向各自数组,然后判断当前三个元素的大小关系以及进行相应的运算或者应用数据去计算,之后根据三个元素的大小关系进行角标转移

放到本题中

首先我们要理解题意

题意是让我们求三个数组中放到一个数组排序后,出现的最小值到最大值距离最短的是多少

比如在这里插入图片描述
a b c之间满足
最小值a 到 最大值c距离最小
并且之间包含b数组的元素

或者是
b a c也可以

总之就是
最小值和最大值并且中间包含一个与这两个集合不一样的集合元素
的距离值 ✖ 2就是答案

#include 
#include 
#include 

using namespace std;

typedef long long LL;

const int N = 100010;

int l, m, n;
int a[N], b[N], c[N];

int main()
{
    scanf("%d%d%d", &l, &m, &n);
    for (int i = 0; i < l; i ++ ) scanf("%d", &a[i]);
    for (int i = 0; i < m; i ++ ) scanf("%d", &b[i]);
    for (int i = 0; i < n; i ++ ) scanf("%d", &c[i]);

    LL res = 1e18;
    for (int i = 0, j = 0, k = 0; i < l && j < m && k < n;)
    {
        int x = a[i], y = b[j], z = c[k];
        res = min(res, (LL)max(max(x, y), z) - min(min(x, y), z));
        if (x <= y && x <= z) i ++ ;
        else if (y <= x && y <= z) j ++ ;
        else k ++ ;
    }

    printf("%lld\n", res * 2);
    return 0;
}

十四、摩尔投票法

1. 数组中出现次数超过一半的数字

在这里插入图片描述

class Solution {
public:
    int moreThanHalfNum_Solution(vector<int>& nums) {
        int cnt = 0, val;
        for (auto x: nums) {
            if (!cnt) val = x, cnt ++ ;
            else if (x == val) cnt ++ ;
            else cnt -- ;
        }

        return val;
    }
};

十五、DFS

1. 全排列( 北京大学考研机试题 )

在这里插入图片描述

#include 
#include 
#include 

using namespace std;

const int N = 10;

int n;
char str[N], path[N];
bool st[N];

void dfs(int u)
{
    if (u == n) cout << path << endl;
    else
    {
        for (int i = 0; i < n; i ++ )
            if (!st[i])
            {
                path[u] = str[i];
                st[i] = true;
                dfs(u + 1);
                st[i] = false;
            }
    }
}

int main()
{
    cin >> str;
    n = strlen(str);
    dfs(0);
    return 0;
}

2. 八皇后( 北京大学考研机试题 )

在这里插入图片描述

#include

using namespace std;

const int N = 100;

int g[N][N];
bool l[N*2],ll[N*2],lll[N*2];
int d[N],idx;
int n;

void dfs(int u,int sum)
{
    if(u>8)
    {
        d[++idx] = sum;
    }
    else
    {
        for(int i = 1; i <= 8; i++)
        {
            if(l[i] == false && ll[i+u] == false && lll[i-u+9] == false)
            {
                l[i] = true;
                lll[i-u+9] = true;
                ll[i+u] = true;
                dfs(u+1,sum*10+i);
                l[i] = false;
                lll[i-u+9] = false;
                ll[i+u] = false;
            }
        }
    }
}

int main()
{
    cin >> n;
    
    dfs(1,0);
    
    while(n--)
    {
        int x;
        cin >> x;
        cout << d[x] << endl;
    }
    
    return 0;
}

高精度计算核心就是

通过数组模拟出草稿纸上的运算过程

具体逻辑可以搜csdn

十六、模拟

1. 反序输出( 清华大学考研机试题 )

考点:reverse(s.begin(), s.end());

考研数据结构上机题【36个模块77道题】5万字帮助你学会考研算法【完结篇】_第24张图片

#include 
#include 
#include 

using namespace std;

int main()
{
    string s;
    while (cin >> s)
    {
        reverse(s.begin(), s.end());
        cout << s << endl;
    }
    return 0;
}

2. 特殊乘法( 清华大学考研机试题 )

考研数据结构上机题【36个模块77道题】5万字帮助你学会考研算法【完结篇】_第25张图片

#include 
#include 
#include 

using namespace std;

int main()
{
    string a, b;
    cin >> a >> b;
    int res = 0;
    for (auto x: a)
        for (auto y: b)
            res += (x - '0') * (y - '0');
    cout << res << endl;
    return 0;
}

3. 众数( 清华大学考研机试题 )

考研数据结构上机题【36个模块77道题】5万字帮助你学会考研算法【完结篇】_第26张图片

#include 
#include 
#include 

using namespace std;

int s[6][10];

int main()
{
    int n, m;
    scanf("%d%d", &n, &m);
    while (n -- )
    {
        int x;
        scanf("%d", &x);
        for (int i = 0; i < m; i ++ )
        {
            s[i][x % 10] ++ ;
            x /= 10;
        }
    }

    for (int i = 0; i < m; i ++ )
    {
        int k = 0;
        for (int j = 0; j < 10; j ++ )
            if (s[i][k] < s[i][j])
                k = j;
        printf("%d\n", k);
    }

    return 0;
}

4. 糖果分享游戏

考研数据结构上机题【36个模块77道题】5万字帮助你学会考研算法【完结篇】_第27张图片

#include
#include
using namespace std;
const int N=110;
int candy[N],bak[N];
int n,count;
bool isequal()
{
    for(int i=1;i<n;i++)
        if(candy[i]!=candy[i-1])
            return false;
    return true;
}
void share()
{
    memset(bak,0,sizeof(bak));
    for(int i=0;i<n;i++)
        bak[(i+1)%n]=candy[i]/2;
    for(int i=0;i<n;i++)
        candy[i]=candy[i]/2+bak[i];
    for(int i=0;i<n;i++)
        if(candy[i]%2)
            candy[i]=candy[i]+1;
}
int main()
{
    scanf("%d",&n);
    while(n!=0)
    {
        for(int i=0;i<n;i++) scanf("%d",&candy[i]);

        count=0;

        while(!isequal())
        {
            share();
            count++;
        }
        printf("%d %d\n",count,candy[0]);

        scanf("%d",&n);
    }

}

十七、递推

1. 递推数列( 清华大学考研机试题 )

考研数据结构上机题【36个模块77道题】5万字帮助你学会考研算法【完结篇】_第28张图片

#include 
#include 
#include 

using namespace std;

const int N = 10010, MOD = 10000;

int a[N];

int main()
{
    int p, q, k;
    cin >> a[0] >> a[1] >> p >> q >> k;
    for (int i = 2; i <= k; i ++ )
        a[i] = (p * a[i - 1] + q * a[i - 2]) % MOD;
    cout << a[k] % MOD << endl;

    return 0;
}

2. 吃糖果( 北京大学考研机试题 )

考研数据结构上机题【36个模块77道题】5万字帮助你学会考研算法【完结篇】_第29张图片

#include
using namespace std;

int main()
{
    int n,f[21];
    cin>>n;
    f[0]=1;
    f[1]=1;
    for(int i=2;i<=n;i++)f[i]=f[i-1]+f[i-2];
    cout<<f[n];
}

十八、BFS

1. 玛雅人的密码

在这里插入图片描述
整体思路是用BFS暴搜
存在队列中的基本元素是pair

  • 前者表示经过多次交换后当下的字符串的组成
  • 后者表示达到当下的字符串状态,经过的交换次数

因为数字本身可能会重复出现,而且对于一个字符串交换它两个不同相邻位置得到的新字符串,也可能产生重复,但我们要求的是最少交换次数,因为BFS的性质,保证了可以实现最小,所以可以设置一个map映射组,来存储已经出现过的字符串情况,防止重复对相同组成的字符串再入队处理,以作到剪枝的作用,降低时间复杂度。

这道题进入BFS暴搜前,要做一些基本的判断,很容易就考虑不到,要做好了。

#include 
#include 
#include 
#include 
#include 

using namespace std;

int n;

int bfs(string start)
{
    queue<string> q;
    unordered_map<string, int> dist;
    dist[start] = 0;
    q.push(start);

    while (q.size())
    {
        auto t = q.front();
        q.pop();

        for (int i = 0; i < n; i ++ )
            if (t.substr(i, 4) == "2012")
                return dist[t];

        for (int i = 1; i < n; i ++ )
        {
            string r = t;
            swap(r[i], r[i - 1]);
            if (!dist.count(r))
            {
                dist[r] = dist[t] + 1;
                q.push(r);
            }
        }
    }
    return -1;
}

int main()
{
    string start;
    cin >> n >> start;
    cout << bfs(start) << endl;
    return 0;
}

2. 等差数列

在这里插入图片描述
本题的思路由样例入手

根据样例分析出
只要每行有两个元素
那么该行就能推出所有等差数据

同理列也是

所以我们先遍历所有行和列
看哪些有两个元素的

然后根据这样行和列进行填充
填充过程中观察是否新数据使得对应的行或者列
变得大于两个元素,进而可以填充

那么该过程就想到了bfs

bfs队列存储的元素是什么呢?就是所在的行或者列

怎么判断是行还是列呢?
行的范围为(0-n)
列的范围(n,m+n)

#include 
#include 
#include 

#define x first
#define y second

using namespace std;

typedef pair<int, int> PII;

const int N = 1010, M = N * 2;

int n, m;
int row[N], col[N];
int q[M], hh, tt = -1;
bool st[M];
PII ans[N * N];
int top;
int g[N][N];

int main()
{
    scanf("%d%d", &n, &m);
    for (int i = 0; i < n; i ++ )
        for (int j = 0; j < m; j ++ )
        {
            scanf("%d", &g[i][j]);
            if (g[i][j])
            {
                row[i] ++ ;
                col[j] ++ ;
            }
        }

    for (int i = 0; i < n; i ++ )
        if (row[i] >= 2 && row[i] < m)
        {
            q[ ++ tt] = i;
            st[i] = true;
        }
    for (int i = 0; i < m; i ++ )
        if (col[i] >= 2 && col[i] < n)
        {
            q[ ++ tt] = i + n;
            st[i + n] = true;
        }

    while (hh <= tt)
    {
        auto t = q[hh ++ ];
        if (t < n)  // 行
        {
            PII p[2];
            int cnt = 0;
            for (int i = 0; i < m; i ++ )
                if (g[t][i])
                {
                    p[cnt ++ ] = {i, g[t][i]};
                    if (cnt == 2) break;
                }
            int d = (p[1].y - p[0].y) / (p[1].x - p[0].x);
            int a = p[1].y - d * p[1].x;

            for (int i = 0; i < m; i ++ )
                if (!g[t][i])
                {
                    g[t][i] = a + d * i;
                    ans[top ++ ] = {t, i};
                    col[i] ++ ;
                    if (col[i] >= 2 && col[i] < m && !st[i + n])
                    {
                        q[ ++ tt] = i + n;
                        st[i + n] = true;
                    }
                }
        }
        else  // 列
        {
            t -= n;
            PII p[2];
            int cnt = 0;
            for (int i = 0; i < n; i ++ )
                if (g[i][t])
                {
                    p[cnt ++ ] = {i, g[i][t]};
                    if (cnt == 2) break;
                }
            int d = (p[1].y - p[0].y) / (p[1].x - p[0].x);
            int a = p[1].y - d * p[1].x;
            for (int i = 0; i < n; i ++ )
                if (!g[i][t])
                {
                    g[i][t] = a + d * i;
                    ans[top ++ ] = {i, t};
                    row[i] ++ ;
                    if (row[i] >= 2 && row[i] < n && !st[i])
                    {
                        q[ ++ tt] = i;
                        st[i] = true;
                    }
                }
        }
    }

    sort(ans, ans + top);
    for (int i = 0; i < top; i ++ )
    {
        auto& p = ans[i];
        printf("%d %d %d\n", p.x + 1, p.y + 1, g[p.x][p.y]);
    }

    return 0;
}

十九、字符串处理

1. 首字母大写

在这里插入图片描述

#include 
#include 
#include 

using namespace std;

int main()
{
    string s;
    getline(cin, s);
    for (int i = 0; i < s.size(); i ++ )
        if (!i || s[i - 1] == ' ')
            cout << (char)toupper(s[i]);
        else
            cout << s[i];
    return 0;
}

2. 日志排序

在这里插入图片描述
在这里插入图片描述

#include 
#include 
#include 
#include 

using namespace std;

const int N = 10010;

int n;
string logs[N];

int main()
{
    while (getline(cin, logs[n]))
        if (logs[n].size()) n ++ ;
        else break;

    sort(logs, logs + n, [](string& a, string &b) {
        stringstream ssina(a), ssinb(b);
        string sa[4], sb[4];
        for (int i = 0; i < 4; i ++ )
        {
            ssina >> sa[i];
            ssinb >> sb[i];
        }

        if (sa[3] == sb[3]) return sa[1] + sa[2] < sb[1] + sb[2];

        double ta, tb;
        sscanf(sa[3].c_str(), "%lf(s)", &ta);
        sscanf(sb[3].c_str(), "%lf(s)", &tb);
        return ta < tb;
    });

    for (int i = 0; i < n; i ++ )
        cout << logs[i] << endl;

    return 0;
}

3. 字符串转换整数

在这里插入图片描述

#include 
#include 
#include 
#include 

using namespace std;

typedef long long LL;

int main()
{
    string s;
    cin >> s;

    for (int i = 0; i < s.size(); i ++ )
        if (isdigit(s[i]))
        {
            LL x = 0;
            while (i < s.size() && isdigit(s[i]))
            {
                x = x * 10 + s[i] - '0';
                if (x > INT_MAX)
                {
                    puts("-1");
                    return 0;
                }
                i ++ ;
            }
            cout << x << endl;
            return 0;
        }

    puts("-1");
    return 0;
}

二十、递归,画图

1. 重复者

在这里插入图片描述
本题由于第三次图形需要知道 第二层的图形

所以需要递归

#include 
#include 
#include 
#include 

using namespace std;

int n;
vector<string> p;

vector<string> g(int k)
{
    if (k == 1) return p;
    auto s = g(k - 1);
    int m = s.size();

    vector<string> res(n * m);
    for (int i = 0; i < n * m; i ++ )
        res[i] = string(n * m, ' ');

    for (int i = 0; i < n; i ++ )
        for (int j = 0; j < n; j ++ )
            if (p[i][j] != ' ')
                for (int x = 0; x < m; x ++ )
                    for (int y = 0; y < m; y ++ )
                        res[i * m + x][j * m + y] = s[x][y];

    return res;
}

int main()
{
    while (cin >> n, n)
    {
        p.clear();
        getchar();  // 读掉n后的回车
        for (int i = 0; i < n; i ++ )
        {
            string line;
            getline(cin, line);
            p.push_back(line);
        }

        int k;
        cin >> k;
        auto res = g(k);
        for (auto& s: res) cout << s << endl;
    }

    return 0;
}

二十一、背包问题

1. 01背包

在这里插入图片描述

#include 
#include 
#include 

using namespace std;

const int N = 1010;

int n, m;
int f[N][N];

int main()
{
    scanf("%d%d", &n, &m);
    for (int i = 1; i <= n; i ++ )
    {
        int v, w;
        scanf("%d%d", &v, &w);
        for (int j = 0; j <= m; j ++ )
        {
            f[i][j] = f[i - 1][j];
            if (j >= v)
                f[i][j] = max(f[i][j], f[i - 1][j - v] + w);
        }
    }

    printf("%d\n", f[n][m]);
    return 0;
}

2. 神奇的口袋( 体积恰好是 )

在这里插入图片描述

#include

using namespace std;

const int N = 50;

int f[N][N];
int a[N];
int n;

int main()
{
    cin >> n;
    
    for(int i = 1; i <= n; i++)
    {
        cin >> a[i];
    }
    
    f[0][0] = 1;
    
    for(int i = 1; i <= n; i++)
    {
        for(int j = 0; j <= 40; j++)
        {
            f[i][j] = f[i-1][j];
            if(a[i]<=j)
            {
                f[i][j] = f[i][j] + f[i-1][j-a[i]];
            }
        }
    }
    
    cout << f[n][40];
    
    return 0;
}

3. 整数拆分

在这里插入图片描述
本题中如果用这种做法的话会爆内存

#include
using namespace std;
typedef long long LL;
const int N = 1e6+10, mod = 1e9;
LL v[25], f[25][N];     //v[]:存储 2 的 i 次幂,即 2^i     

void init(){        //预处理出 2 的 0 次幂到 20 次幂
    v[0] = 1;
    for(int i = 1;i <= 20;++i) v[i] = v[i - 1] * 2;
}

void solve(){
    int n;
    cin >> n;
    init();
    for(int i = 0;i <= 20;++i){
        for(int j = 0;j <= n;++j){
            if(!i || !j){   //状态为 f[i][0] 和 f[0][j] 时,将其初始化为 1
                f[i][j] = 1;
                continue;
            }

            if(j < v[i]) f[i][j] = f[i - 1][j];     //当 2^i > j 时,f[i][j]中只有一个集合(有0个 2^i 的集合)

            else f[i][j] = (f[i - 1][j] + f[i][j - v[i]]) % mod;    //状态转移方程
        }
    }

    cout << f[20][n];   
}

int main(){
    solve();
    return 0;
}

#include 
#include 
#include 

using namespace std;

const int N = 1000010, MOD = 1e9;

int n;
int f[N];

int main()
{
    scanf("%d", &n);
    f[0] = 1;
    for (int i = 1; i <= n; i *= 2)
        for (int j = i; j <= n; j ++ )
            f[j] = (f[j] + f[j - i]) % MOD;
    cout << f[n] << endl;

    return 0;
}

二十二、高精度

1. N的阶乘

在这里插入图片描述

做法:预处理

#include 
#include 
#include 
#include 

using namespace std;

const int N = 1010;

vector<int> F[N];

vector<int> mul(vector<int>& A, int b)
{
    vector<int> C;
    for (int i = 0, t = 0; i < A.size() || t; i ++ )
    {
        if (i < A.size()) t += A[i] * b;
        C.push_back(t % 10);
        t /= 10;
    }
    return C;
}

int main()
{
    int n;
    F[0] = {1};
    for (int i = 1; i <= 1000; i ++ ) F[i] = mul(F[i - 1], i);

    while (cin >> n)
    {
        for (int i = F[n].size() - 1; i >= 0; i -- )
            cout << F[n][i];
        cout << endl;
    }

    return 0;
}

2. 基本算术

在这里插入图片描述

#include 
#include 
#include 
#include 

using namespace std;

int add(vector<int>& A, vector<int>& B)
{
    int res = 0;
    for (int i = 0, t = 0; i < A.size() || i < B.size() || t; i ++ )
    {
        if (i < A.size()) t += A[i];
        if (i < B.size()) t += B[i];
        t /= 10;
        res += t;
    }
    return res;
}

int main()
{
    string a, b;
    while (cin >> a >> b, a != "0" || b != "0")
    {
        vector<int> A, B;
        for (int i = a.size() - 1; i >= 0; i -- ) A.push_back(a[i] - '0');
        for (int i = b.size() - 1; i >= 0; i -- ) B.push_back(b[i] - '0');
        int res = add(A, B);
        if (!res) puts("No carry operation.");
        else if (res == 1) puts("1 carry operation.");
        else printf("%d carry operations.\n", res);
    }

    return 0;
}

3. 整数查询

在这里插入图片描述

#include 
#include 
#include 
#include 

using namespace std;

vector<int> add(vector<int>& A, vector<int>& B)
{
    vector<int> C;
    for (int i = 0, t = 0; i < A.size() || i < B.size() || t; i ++ )
    {
        if (i < A.size()) t += A[i];
        if (i < B.size()) t += B[i];
        C.push_back(t % 10);
        t /= 10;
    }
    return C;
}

int main()
{
    vector<int> A{0};
    string b;
    while (cin >> b, b != "0")
    {
        vector<int> B;
        for (int i = b.size() - 1; i >= 0; i -- )
            B.push_back(b[i] - '0');
        A = add(A, B);
    }

    while (A.size() > 1 && !A.back()) A.pop_back();
    for (int i = A.size() - 1; i >= 0; i -- )
        cout << A[i];
    cout << endl;

    return 0;
}

二十三、因式分解

1. 质因数的个数

考研数据结构上机题【36个模块77道题】5万字帮助你学会考研算法【完结篇】_第30张图片

#include 
#include 
#include 

using namespace std;

int main()
{
    int n;
    while (cin >> n)
    {
        int res = 0;
        for (int i = 2; i * i <= n; i ++ )
            if (n % i == 0)
            {
                while (n % i == 0)
                {
                    n /= i;
                    res ++ ;
                }
            }

        if (n > 1) res ++ ;
        cout << res << endl;
    }

    return 0;
}

2. 约数个数

考研数据结构上机题【36个模块77道题】5万字帮助你学会考研算法【完结篇】_第31张图片

#include 

using namespace std;

int n, x;

int main()
{
    cin >> n;
    while (n -- )
    {
        cin >> x;
        int res = 0;
        for (int i = 1; i <= x / i; i ++)
            if (x % i == 0) 
            {
                res ++ ;
                if (x / i != i) res ++ ;
            }
        cout << res << '\n';
    }

    return 0;
}

3. 阶乘的末尾0(上海交通大学考研机试题)

考研数据结构上机题【36个模块77道题】5万字帮助你学会考研算法【完结篇】_第32张图片
本题考的是理解末尾0

末尾的0 就表示 因数中有多少个10

那么也就是有多少个因数2 和 5

由于因数2的个数一定大于5的个数

所以就看有多少个因数5就可以了

#include 
#include 
#include 

using namespace std;

int main()
{
    int n;
    cin >> n;
    int res = 0;
    while (n / 5) res += n / 5, n /= 5;
    cout << res << endl;

    return 0;
}

4. 整除问题(上海交通大学考研机试题)

考研数据结构上机题【36个模块77道题】5万字帮助你学会考研算法【完结篇】_第33张图片

#include 
#include 
#include 
#include 

using namespace std;

vector<vector<int>> get_ds(int n)
{
    vector<vector<int>> res;
    for (int i = 2; i * i <= n; i ++ )
        if (n % i == 0)
        {
            int s = 0;
            while (n % i == 0) n /= i, s ++ ;
            res.push_back({i, s});
        }

    if (n > 1) res.push_back({n, 1});
    return res;
}

int get_p(int n, int p)
{
    int res = 0;
    while (n / p) res += n / p, n /= p;
    return res;
}

int main()
{
    int n, m;
    cin >> n >> m;

    auto ds = get_ds(m);
    int res = 1e8;

    for (int i = 0; i < ds.size(); i ++ )
    {
        int p = ds[i][0], s = ds[i][1];
        res = min(res, (get_p(n, p) / s));
    }

    cout << res << endl;

    return 0;
}

二十四、枚举

1. 与7无关的数 (北京大学考研机试题)

考研数据结构上机题【36个模块77道题】5万字帮助你学会考研算法【完结篇】_第34张图片

#include 
#include 
#include 

using namespace std;

int main()
{
    int n;
    cin >> n;

    int res = 0;
    for (int i = 1; i <= n; i ++ )
    {
        if (i % 7 == 0 || to_string(i).find('7') != -1)
            continue;
        res += i * i;
    }

    cout << res << endl;

    return 0;
}

2. 打印极值点下标( 北京大学考研机试题 )

考研数据结构上机题【36个模块77道题】5万字帮助你学会考研算法【完结篇】_第35张图片

#include 
#include 
#include 

using namespace std;

const int N = 110;

int n;
int w[N];

int main()
{
    cin >> n;
    for (int i = 0; i < n; i ++ ) cin >> w[i];

    for (int i = 0; i < n; i ++ )
        if (!i)
        {
            if (n == 1 || w[i] != w[i + 1]) cout << i << ' ';
        }
        else if (i == n - 1)
        {
            if (w[i] != w[i - 1]) cout << i << ' ';
        }
        else
        {
            if (w[i] > w[i - 1] && w[i] > w[i + 1] ||
                w[i] < w[i - 1] && w[i] < w[i + 1])
                    cout << i << ' ';
        }

    return 0;
}

3. 最简真分数( 北京大学考研机试题 ) 【真分数】

考研数据结构上机题【36个模块77道题】5万字帮助你学会考研算法【完结篇】_第36张图片
最简真分数

  1. 分子大于分母
  2. 最大公约数是1
#include 
#include 
#include 

using namespace std;

const int N = 110;

int n;
int a[N];

// 欧几里得算法(辗转相除法)
int gcd(int a, int b) {
    return b? gcd(b, a % b): a;
}

int main() {
    while (cin >> n && n) {
        memset(a, 0, sizeof(a));
        int cnt = 0;
        for (int i = 0; i < n; i++) cin >> a[i];
        for (int i = 0; i < n; i++) {
            for (int j = 0; j < n; j++) {
                if (a[i] < a[j] && gcd(a[i], a[j]) == 1) {
                    cnt++;            
                }
            }
        }
        cout << cnt << endl;
    }

    return 0;
}

4. 买房子 ( 北京大学考研机试题 )【算立方和平方】

考研数据结构上机题【36个模块77道题】5万字帮助你学会考研算法【完结篇】_第37张图片

include <iostream>
#include 
#include 

using namespace std;

int main()
{
    double n, k;
    while (cin >> n >> k)
    {
        double sum = n, house = 200;

        bool flag = false;
        for (int i = 1; i <= 21; i ++ )
        {
            if (sum >= house)
            {
                cout << i << endl;
                flag = true;
                break;
            }

            sum += n;
            house *= 1 + k / 100;
        }

        if (!flag) puts("Impossible");
    }

    return 0;
}

5. Special数(北京邮电大学考研机试题)

考研数据结构上机题【36个模块77道题】5万字帮助你学会考研算法【完结篇】_第38张图片

#include 
#include 
#include 
#include 

using namespace std;

int main()
{
    int T;
    cin >> T;

    while (T -- )
    {
        int n;
        cin >> n;

        int res = 0;
        for (int i = 1; i * i * i <= n; i ++ )
        {
            int x = i * i * i;
            int r = sqrt(x);
            if (r * r == x)
                res ++ ;
        }

        cout << res << endl;
    }

    return 0;
}

二十六、位运算

1. 位操作练习(北京大学考研机试题)

考研数据结构上机题【36个模块77道题】5万字帮助你学会考研算法【完结篇】_第39张图片

本题的思路是:
把a右移1位,如果超界,&65535(也就是保留16位)

然后让a + (a未移动的值右移15位的值)
得到的就是循环移动的值了

#include

using namespace std;

int main()
{
    int a,b;
    while(cin >> a >> b)
    {
        bool flag = false;
        for(int i = 0; i < 16;i++)
        {
            int t = a;
            a = (a << 1)&65535;
            a = a + (t>>15);
            if(a==b)
            {
                cout << "YES" << endl;
                flag = true;
            }
        }
        if(flag==false)
        {
            cout << "NO" << endl;
        }
    }
    
    return 0;
}

2. 二进制数(北京邮电大学考研机试题)

考研数据结构上机题【36个模块77道题】5万字帮助你学会考研算法【完结篇】_第40张图片
就是把10进制输出二进制
不输出前导0

#include 
#include 
#include 

using namespace std;

int main()
{
    unsigned int n;
    while (cin >> n)
    {
        string res;
        if (!n) res = "0";
        while (n) res += to_string(n & 1), n >>= 1;
        reverse(res.begin(), res.end());
        cout << res << endl;
    }

    return 0;
}

二十七、矩阵

1. 旋转矩阵(北京航空航天大学考研机试题)

在这里插入图片描述
这道题就是找规律
在这里插入图片描述

#include 
#include 
#include 
#include 

using namespace std;

const int N = 10;

int n;

vector<vector<int>> rotate(vector<vector<int>> a)
{
    auto res = a;

    for (int i = 0; i < n; i ++ )
        for (int j = 0, k = n - 1; j < n; j ++, k -- )
            res[i][j] = a[k][i];

    return res;
}

int main()
{
    cin >> n;

    vector<vector<int>> a(n, vector<int>(n));
    auto b = a;

    for (int i = 0; i < n; i ++ )
        for (int j = 0; j < n; j ++ )
            cin >> a[i][j];

    for (int i = 0; i < n; i ++ )
        for (int j = 0; j < n; j ++ )
            cin >> b[i][j];

    for (int i = 0; i < 4; i ++ )
    {
        if (a == b)
        {
            cout << i * 90 << endl;
            return 0;
        }

        a = rotate(a);
    }

    puts("-1");
    return 0;
}

2. 矩阵幂【矩阵相乘】(北京邮电大学考研机试题)

在这里插入图片描述

两个矩阵相乘结果

两个矩阵a,b相乘得到一个新的矩阵c,新矩阵的第i行第j列的元素是a的第i行和b的第j列的对应元素相乘的求和。

    for(int i=0;i<n;i++)
        for(int j=0;j<n;j++)
            for(int k=0;k<n;k++)
                tmp[i][j]+=a[i][k]*b[k][j];

求矩阵的k次方

注意:1次方就是 矩阵乘单位矩阵

#include 
#include 
#include 
#include 
using namespace std;
const int N=15;
int n,k;
int w[N][N];
// 将矩阵a和b相乘,结果存放到c中。 
void mul(int c[][N],int a[][N],int b[][N]){
    static int tmp[N][N];
    memset(tmp,0,sizeof tmp);
    for(int i=0;i<n;i++)
        for(int j=0;j<n;j++)
            for(int k=0;k<n;k++)
                tmp[i][j]+=a[i][k]*b[k][j];
    memcpy(c,tmp,sizeof tmp); 
}

int main(){
    cin>>n>>k;
    // 读入原始矩阵 
    for(int i=0;i<n;i++)
        for(int j=0;j<n;j++)
            cin>> w[i][j];
    // 将res初始化为单位矩阵 
    int res[N][N]={0};
    for(int i=0;i<n;i++) res[i][i]=1;
    while(k--) mul(res,res,w);
    // 输出结果矩阵 
    for(int i=0;i<n;i++){
        for(int j=0;j<n;j++)
            cout<<res[i][j]<<" ";
        cout<<endl; 
    } 
    return 0;
}

3. C翻转(北京邮电大学考研机试题)

在这里插入图片描述

#include 
#include 
#include 

using namespace std;

const int N = 5;

int n;
int g[N][N];

void rotate(int x, int y, int m)
{
    int w[N][N];
    memcpy(w, g, sizeof g);

    for (int i = 0; i < m; i ++ )
        for (int j = 0, k = m - 1; j < m; j ++, k -- )
            w[i][j] = g[x + k][y + i];

    for (int i = 0; i < m; i ++ )
        for (int j = 0; j < m; j ++ )
            g[x + i][y + j] = w[i][j];
}

int main()
{
    for (int i = 0; i < 5; i ++ )
        for (int j = 0; j < 5; j ++ )
            cin >> g[i][j];

    int a, b, x, y;
    cin >> a >> b >> x >> y;
    x --, y -- ;

    if (a == 1) rotate(x, y, b);
    else
    {
        for (int i = 0; i < 3; i ++ )
            rotate(x, y, b);
    }

    for (int i = 0; i < 5; i ++ )
    {
        for (int j = 0; j < 5; j ++ )
            cout << g[i][j] << ' ';
        cout << endl;
    }

    return 0;
}

二十八、计算几何

1. 球的计算

在这里插入图片描述

#include 
#include 
#include 
#include 

using namespace std;

const double PI = acos(-1);

int main()
{
    int T;
    cin >> T;
    while (T -- )
    {
        double x0, y0, z0, x1, y1, z1;
        cin >> x0 >> y0 >> z0 >> x1 >> y1 >> z1;

        double dx = x0 - x1;
        double dy = y0 - y1;
        double dz = z0 - z1;

        double r = sqrt(dx * dx + dy * dy + dz * dz);
        double v = 4.0 / 3 * PI * r * r * r;

        printf("%.2lf %.2lf\n", r, v);
    }
    return 0;
}

2. 点的距离

在这里插入图片描述

重新定义类的相减

#include 
#include 
#include 
#include 

using namespace std;

class CPoint
{
public:
    double x, y;

    CPoint(){}
    CPoint(double _x, double _y): x(_x), y(_y) {}

    double operator- (const CPoint& t) const
    {
        double dx = x - t.x;
        double dy = y - t.y;
        return sqrt(dx * dx + dy * dy);
    }
};

int main()
{
    int T;
    cin >> T;

    while (T -- )
    {
        CPoint a, b;
        cin >> a.x >> a.y >> b.x >> b.y;
        printf("%.2lf\n", a - b);
    }

    return 0;
}

3. 直角三角形

在这里插入图片描述

#include 
#include 
#include 
#include 

using namespace std;

struct CPoint
{
    double x, y;

    double operator- (const CPoint& t) const
    {
        double dx = x - t.x;
        double dy = y - t.y;
        return sqrt(dx * dx + dy * dy);
    }
};

struct CTriangle
{
    CPoint a, b, c;

    bool check()
    {
        double d[3] = {a - b, b - c, a - c};
        sort(d, d + 3);
        return fabs(d[0] * d[0] + d[1] * d[1] - d[2] * d[2]) < 1e-8;
    }

    double get_len()
    {
        return (a - b) + (b - c) + (a - c);
    }
};

int main()
{
    int T;
    cin >> T;
    while (T -- )
    {
        CTriangle t;
        cin >> t.a.x >> t.a.y >> t.b.x >> t.b.y >> t.c.x >> t.c.y;
        if (t.check()) puts("Yes");
        else puts("No");
        printf("%.2lf\n", t.get_len());
    }

    return 0;
}

二十九、前缀和

1. 最长平衡串(北京邮电大学考研机试题)

在这里插入图片描述
在这里插入图片描述

#include 
using namespace std;

const int N = 1e6 + 10;
int a[N], b[N];
int c[N];
int n;
unordered_map<int, int> st;

int main()
{
    string x;
    cin >> x;
    n = x.size();

    for(int i = 1; i <= n; i ++ ){
        a[i] = a[i - 1], b[i] = b[i - 1];
        if(x[i - 1] == '1') a[i] ++ ;
        else b[i] ++ ;
    }

    int res = 0;
    st[0] = 0;  //初始化哈希表头, 将从0开始, 到0结束的子串记为0
    for(int i = 1; i <= n; i ++ ){
        c[i] = a[i] - b[i];
        if(st.count(c[i]) != 0) res = max(res, a[i] - a[st[c[i]]]);
        else st[c[i]] = i;
    }


    printf("%d", res * 2);

    return 0;
}

三十、推公式

1. 数字台阶

在这里插入图片描述
在这里插入图片描述

#include 
#include 
#include 

using namespace std;
int check(int x,int y)
{
   return x*2-(x%2);

}
int main()
{
    int x,y,n;
    cin>>n;
    while(n--)
    {
        cin>>x>>y;
    if(x==y)cout<<check(x,y)<<endl;
    else if(x==y+2)cout<<check(x-2,y)+2<<endl;
    else puts("No Number");
    }
    return 0;
}

2. 整数和

在这里插入图片描述

推等差公式

#include
using namespace std;

int main()
{
    int n,x,c=0;
    cin>>n;
    while(n--){
        cin>>x;
        c=(abs(x)+1)*3*x/2;//要考虑负数情况,所以一开始要设绝对值
        cout<<c<<endl;
    }
}

3. 弹地小球

在这里插入图片描述

#include 
#include 
#include 

using namespace std;

int main()
{
    int T;
    cin >> T;
    while (T -- )
    {
        double h;
        int n;
        cin >> h >> n;
        double s = h;
        for (int i = 0; i < n - 1; i ++ )
        {
            s += h;
            h /= 2;
        }

        printf("%.2lf\n", s);
    }

    return 0;
}

三十一、最短路

1. 我想回家(北京大学考研机试题)

在这里插入图片描述
求城市1到所属阵营城市的最短路径,记录在dist1[]中
求城市2到所属阵营城市的最短路径,记录在dist2[]中
遍历所有边,边的两端的城市i、j分别属于阵营1、2, 该边的权重为w
那么本题可以等价于求解:
min(w+dist[i]+dist[j])

#include 
#include 
#include 

using namespace std;

const int N=610,M=20010,INF=0x3f3f3f3f;
int n,m;
int h[N],e[M],w[M],ne[M],idx;
int q[N],dist1[N],dist2[N];
bool st[N];
int team[N];

void add(int a,int b,int c){
    e[idx]=b;
    w[idx]=c;
    ne[idx]=h[a];
    h[a]=idx++;
}

void spfa(int start,int dist[]){
    int hh=0,tt=1;
    q[0]=start;
    memset(dist,0x3f,sizeof dist1);
    dist[start]=0;

    while(hh!=tt){
        // 队头元素出队 
        int t=q[hh++];   
        if(hh==N) hh=0;  // 循环队列 
        st[t]=false;
        // 遍历该元素的所有邻接点 
        for(int i=h[t];i!=-1;i=ne[i]){
            int j=e[i];
            if(team[j]!=start)continue; // 如果是不同阵营的则跳过 
            if(dist[j]>dist[t]+w[i]){
                dist[j]=dist[t]+w[i];  // 更新,入队 
                if(!st[j]){
                    q[tt++]=j;
                    if(tt==N) tt=0; // 循环队列 
                    st[j]=true;
                }
            }
        }
    }
}
int main(){
    while(scanf("%d",&n),n){
        scanf("%d",&m);
        memset(h,-1,sizeof h),idx=0;
        while(m--){
            int a,b,c;
            scanf("%d%d%d",&a,&b,&c);
            add(a,b,c),add(b,a,c);   // 无向图,添加双向边 
        } 
        for(int i=1;i<=n;i++) scanf("%d",&team[i]);  // 输入所属的团队 
        spfa(1,dist1);
        spfa(2,dist2);
        int res=INF;
        for(int i=0;i<idx;i++){
            int a=e[i^1],b=e[i];// 方向相反的一对边 
            if(team[a]==1 && team[b]==2) // 如果该边连接的两个城市属于不同阵营,则可以更新边 
                res=min(res,dist1[a]+w[i]+dist2[b]); 
        }
        if(res==INF) puts("-1");
        else cout<<res<<endl; 
    }
    return 0;
}

2. 最短路径

在这里插入图片描述

#include 
#include 
#include 

using namespace std;

const int N = 110, MOD = 100000, INF = 0x3f3f3f3f;

int n, m;
int p[N];
int d[N][N];

int find(int x)
{
    if (p[x] != x) p[x] = find(p[x]);
    return p[x];
}

int main()
{
    cin >> n >> m;
    for (int i = 0; i < n; i ++ ) p[i] = i;

    memset(d, 0x3f, sizeof d);
    for (int i = 0; i < n; i ++ ) d[i][i] = 0;

    for (int i = 0, len = 1; i < m; i ++, len = len * 2 % MOD)
    {
        int a, b;
        cin >> a >> b;
        if (find(a) != find(b))
        {
            p[find(a)] = find(b);
            d[a][b] = d[b][a] = len;
        }
    }

    for (int k = 0; k < n; k ++ )
        for (int i = 0; i < n; i ++ )
            for (int j = 0; j < n; j ++ )
                d[i][j] = min(d[i][j], d[i][k] + d[k][j]);

    for (int i = 1; i < n; i ++ )
        if (d[0][i] == INF) puts("-1");
        else cout << d[0][i] % MOD << endl;

    return 0;
}

3. 最短路径

在这里插入图片描述

#include 
#include 
#include 

using namespace std;

const int N = 55, M = N * N / 2, INF = 0x3f3f3f3f;

int n, m, Q;
int g[N][N], d[N][N];
struct Edge
{
    int a, b;
}e[M];

void floyd()
{
    memcpy(d, g, sizeof d);

    for (int k = 1; k <= n; k ++ )
        for (int i = 1; i <= n; i ++ )
            for (int j = 1; j <= n; j ++ )
                d[i][j] = min(d[i][j], d[i][k] + d[k][j]);
}

int main()
{
    int T;
    cin >> T;
    while (T -- )
    {
        cin >> n >> m >> Q;
        memset(g, 0x3f, sizeof g);
        for (int i = 0; i < n; i ++ ) g[i][i] = 0;

        for (int i = 1; i <= m; i ++ )
        {
            int a, b, c;
            cin >> a >> b >> c;
            e[i] = {a, b};
            g[a][b] = g[b][a] = c;
        }

        floyd();
        printf("%d\n", d[1][n]);

        while (Q -- )
        {
            int t;
            cin >> t;
            int a = e[t].a, b = e[t].b;
            g[a][b] = g[b][a] = INF;
        }

        floyd();
        if (d[1][n] == INF) puts("-1");
        else cout << d[1][n] << endl;
    }

    return 0;
}

三十二、思维题

1. 棋盘遍历问题(上海交通大学考研机试题)

考研数据结构上机题【36个模块77道题】5万字帮助你学会考研算法【完结篇】_第41张图片
1.n1&&m1时,答案显然是Y

2.接着n1||m1时,但棋子走出去后,显然没有回头的路,答案应该是N

3.接着是n%2==0时,棋子可以先一直走到底,再把最后一行遍历完,再是倒数第二行,倒数第三…可以发现,棋子从倒数第一行开始的转向分别是右左右左右左右左…只要最后是向左,就可以遍历完,即n为偶数,答案为Y
示意图:
1 2 3
4 5 6
7 8 9
10 11 12
顺序依次为:1 4 7 10 11 12 9 8 5 6 3 2 1

4.然后是m%2==0时,棋子也是先走到底,再把第二列除去第一行的棋子遍历完,接着是第三,第四,第五列(都不遍历第一行)…最后再把第一行遍历了,即可到达终点,答案为Y
示意图:
1 2 3 4
5 6 7 8
9 10 11 12
顺序依次为:1 5 9 10 6 7 11 12 8 4 3 2 1
5.最后剩下的就是无解情况,答案为N

#include 
using namespace std;
typedef long long ll;
const ll N=1e5+1e4,M=1e3+1e2;
const ll Maxn=0x3ffffff,Minm=-0x3ffffff;
ll n,m;
signed main()
{
    while(cin>>n>>m)
    {
        if(n==1&&m==1)cout<<"Y";//情况1
        else if(n==1||m==1)cout<<"N";//情况2
        else if(n%2==0||m%2==0)cout<<"Y";//情况3,4
        else cout<<"N";//情况5
        cout<<"\n";
    }
}

三十三、哈希表

1. 子串计算(北京大学考研机试题)

考研数据结构上机题【36个模块77道题】5万字帮助你学会考研算法【完结篇】_第42张图片

map自动排序

#include 
#include 
#include 
#include 

using namespace std;

int main()
{
    string str;
    while (cin >> str)
    {
        map<string, int> hash;
        for (int i = 0; i < str.size(); i ++ )
        {
            string s;
            for (int j = i; j < str.size(); j ++ )
            {
                s += str[j];
                hash[s] ++ ;
            }
        }

        for (auto& [k, v]: hash)
            if (v > 1)
                cout << k << ' ' << v << endl;
    }

    return 0;
}

2. 查找(北京邮电大学考研机试题)

unordered_set(通过count(x)==0判断是否存在)

考研数据结构上机题【36个模块77道题】5万字帮助你学会考研算法【完结篇】_第43张图片

#include 
#include 
#include 
#include 

using namespace std;

int main()
{
    int n, m;
    cin >> n;

    unordered_set<int> S;
    while (n -- )
    {
        int x;
        cin >> x;
        S.insert(x);
    }

    cin >> m;

    while (m -- )
    {
        int x;
        cin >> x;
        if (S.count(x)) puts("YES");
        else puts("NO");
    }

    return 0;
}

3. 单词识别(北京理工大学考研机试题)

考研数据结构上机题【36个模块77道题】5万字帮助你学会考研算法【完结篇】_第44张图片

#include 
#include 
#include 
#include 

using namespace std;

int main()
{
    string str;
    getline(cin, str);

    map<string, int> hash;
    for (int i = 0; i < str.size(); i ++ )
    {
        if (isalpha(str[i]))
        {
            int j = i;
            string word;
            while (j < str.size() && isalpha(str[j]))
                word += tolower(str[j ++ ]);
            hash[word] ++ ;
            i = j;
        }
    }

    for (auto& [k, v]: hash)
        cout << k << ':' << v << endl;

    return 0;
}

三十四、双指针

1. 最小面积子矩阵

考研数据结构上机题【36个模块77道题】5万字帮助你学会考研算法【完结篇】_第45张图片

算法1:二维前缀和

#include 
using namespace std;

const int N = 110, INF = 0x3f3f3f3f;

int n, m, k, a[N][N], s[N][N], res = INF;

int main()
{
    cin >> n >> m >> k;

    for(int i = 1; i <= n; i ++)
    {
        for(int j = 1; j <= m; j ++) cin >> a[i][j];
    }

    //预处理前缀和
    for(int i = 1; i <= n; i ++)
    {
        for(int j = 1; j <= m; j ++)
        {
            s[i][j] = s[i - 1][j] + s[i][j - 1] - s[i - 1][j - 1] + a[i][j];
        }
    }

    //枚举每个矩形
    for(int a = 1; a <= n; a ++)
    {
        for(int b = 1; b <= m; b ++)
        {
            for(int c = 1; c <= n; c ++)
            {
                for(int d = 1; d <= m; d ++)
                {
                    int ts = s[c][d] - s[c][b - 1] - s[a - 1][d] + s[a - 1] [b - 1]; //矩形内数字和

                    int tc = (d - b + 1) * (c - a + 1); //矩形面积

                    if(ts >= k) res = min(res, tc); //如果数字和大于等于k更新答案
                }
            }
        }
    }

    if(res == INF) cout << -1 << endl;
    else cout << res << endl;

    return 0;
}

算法2:一维前缀和 + 双指针

#include 
using namespace std;

const int N = 110, INF = 0x3f3f3f3f;

int n, m, k, res = INF;
int g[N][N], s[N][N];

int main()
{
    cin >> n >> m >> k;

    for(int i = 1; i <= n; i ++)
    {
        for(int j = 1; j <= m; j ++) cin >> g[i][j];
    }

    //s[j][i]表示第i列的前缀和数组
    for(int j = 1; j <= m; j ++)
    {
        for(int i = 1; i <= n; i ++) s[j][i] = s[j][i - 1] + g[i][j];
    }

    for(int i = 1; i <= n; i ++)
    {
        for(int j = i; j <= n; j ++)
        {
            for(int l = 1, r = 1, sum = 0; r <= m; r ++) //双指针
            {
                sum += (s[r][j] - s[r][i - 1]);

                while(sum - (s[l][j] - s[l][i - 1]) >= k) 
                {
                    sum -= (s[l][j] - s[l][i - 1]);
                    l ++;
                }

                if(sum >= k)res = min(res, (r - l + 1) * (j - i + 1));
            }
        }
    }

    if(res == INF) puts("-1");
    else cout << res << endl;

    return 0;
}

三十五、序列型DP

1. 最大序列和(清华大学考研机试题)

考研数据结构上机题【36个模块77道题】5万字帮助你学会考研算法【完结篇】_第46张图片
这道题目实际上就是求一串数字中,某一段数字和,使其最大
因此,我们可以使用dp来完成
令:dp[i]为以a[i]结尾的最大序列和
则dp[i]可以为 以a[i-1]结尾的最大序列和加上a[i] 与 a[i]中的最大值
即该题的动态转移方程式可以为:dp[i]=max(dp[i-1]+a[i],dp[i]);

#include 
using namespace std;
typedef long long ll;
const ll N=1e5+1e6;
const ll Min=-0x3ffffff;//负∞
ll n,ans=Min;//  【注】:本题涉及到负数,为保证后面的ans=max(ans,dp[i]),ans一开始因赋值为负∞
ll dp[N],a[N];
signed main()
{
    cin>>n;
    for(ll i=1;i<=n;i++)cin>>a[i],dp[i]=a[i];
    for(ll i=2;i<=n;i++)dp[i]=max(dp[i],dp[i-1]+a[i]);//动态转移方程式
    for(ll i=1;i<=n;i++)ans=max(ans,dp[i]);
    cout<<ans;
}

2. 最长ZigZag子序列

考研数据结构上机题【36个模块77道题】5万字帮助你学会考研算法【完结篇】_第47张图片

#include 
using namespace std;

const int N = 55, INF = 0x3f3f3f3f;

//f[i]表示以第i个数字结尾并且是前一个数字上升得到的a[i]
//g[i]表示以第i个数字结尾并且是前一个数字下降得到的a[i]
//集合划分:【只有一个a[i]】【其他:倒数第二个元素是第j个数字并且需要是下降得到a[j]:g[j] + 1】
//状态计算:f[i] = max(f[i], g[j] + 1);
int f[N], g[N];
int n, a[N], res = -INF;

int main()
{
    cin >> n;

    for(int i = 1; i <= n; i ++) cin >> a[i];

    for(int i = 1; i <= n; i ++)
    {
        f[i] = g[i] = 1;

        for(int j = 1; j < i; j ++)
        {
            if(a[j] > a[i]) f[i] = max(f[i], g[j] + 1);
            if(a[j] < a[i]) g[i] = max(g[i], f[j] + 1);
        }

        res = max(res, max(f[i], g[i]));
    }

    cout << res << endl;

    return 0;
}

三十六、红黑树和并查集

1. 合并集合

在这里插入图片描述

#include

using namespace std;

const int N=100010;
int p[N];//定义多个集合

int find(int x)
{
    if(p[x]!=x) p[x]=find(p[x]);
    /*
    经上述可以发现,每个集合中只有祖宗节点的p[x]值等于他自己,即:
    p[x]=x;
    */
    return p[x];
    //找到了便返回祖宗节点的值
}

int main()
{
    int n,m;
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++) p[i]=i;
    while(m--)
    {
        char op[2];
        int a,b;
        scanf("%s%d%d",op,&a,&b);
        if(*op=='M') p[find(a)]=find(b);//集合合并操作
        else
        if(find(a)==find(b))
        //如果祖宗节点一样,就输出yes
        printf("Yes\n");
        else
        printf("No\n");
    }
    return 0;
}

你可能感兴趣的:(考研408-数据结构(笔试),考研,数据结构,算法)