【ACM专项练习#03】打印图形、栈的合法性、链表操作、dp实例

发布时间 2023-08-06 09:53:35作者: dayceng

运营商活动

题目描述

小明每天的话费是1元,运营商做活动,手机每充值K元就可以获赠1元,一开始小明充值M元,问最多可以用多少天? 注意赠送的话费也可以参与到奖励规则中

输入

输入包括多个测试实例。每个测试实例包括2个整数M,K(2<=k<=M<=1000)。M=0,K=0代表输入结束。

输出

对于每个测试实例输出一个整数,表示M元可以用的天数。

样例输入
2 2
4 3
13 3
0 0
样例输出
3
5
19
提示

注意第三组数据「13 3」结果为什么是19呢, 13/3=4,获得4元奖励。 13%3=1,还剩下1元,4+1=5,5元继续参加奖励规则。 5/3=1,获得1元奖励。 5%3=2,剩下2元,1+2=3,3元继续参与奖励规则。 3/3=1,获得1元奖励。 3%3=0,剩下0元,1+0=1。 1元不能参与剩下奖励。所以一共可以使用的天数是 13+4+1+1=19

代码

在每次充值后,小明会获得额外的1元话费,因此他的总话费是充值金额 M 加上额外的话费。如果我们把每次充值后获得的额外话费也视为一次充值,那么小明每次充值后都会获得额外的 k 元话费。

假设小明一开始充值了 M 元钱,那么他会获得 M / k 元额外话费,以及 M % k 元剩余的钱。其中,M % k 就表示小明无法凑成一次充值的剩余金额。

接着,小明可以利用获得的 M / k 元额外话费再次充值,于是他又会获得 (M / k) / k 元额外话费和 (M / k) % k 元剩余的钱。以此类推,小明可以一直通过充值来获得额外的话费。每次充值所获得的额外话费都是上一次剩余的钱除以 k 得到的。

因此,我们可以用一个 while 循环来模拟小明不断充值的过程,直到无法再获得额外的话费为止。在每次循环中,我们需要计算出当前剩余的钱数 count(包括上一轮的剩余钱数和赠送的额外话费),并将其除以 k 得到额外的话费和剩余的钱数(即下一轮的 M 和 M % k)。最终,小明的总话费就是所有充值所获得的额外话费和剩余话费之和。

因此,m % k 代表了小明一开始充值后剩余的钱数,即无法凑成一次充值的金额,也可以看作是额外话费的一部分。

为什么要凑成一次充值的金额?

在题目中,每充值 K 元就可以获赠 1 元话费,因此,当小明的钱数不能凑成一次充值的金额时,他就无法获得额外的话费了。为了最大化利用每次充值可以获得的额外话费,小明需要确保每次充值的金额都是 K 的整数倍。这样,他就可以获得尽可能多的额外话费,从而最大化使用他的钱数。

例如,假设小明一开始充值了 20 元钱,K=5,那么他可以分别在第 5、10、15、20 天充值,每次充值 5 元,这样他就可以获得 4 元额外话费,总共获得 24 元话费,可以用于 24 天。

如果他在第 4 天充值了 4 元,那么他只能获得 3 元额外话费,总共获得 23 元话费,只能用于 23 天。因此,为了最大化使用他的钱数,小明需要确保每次充值的金额都是 K 的整数倍,也就是凑成一次充值的金额。

#include<iostream>

using namespace std;
int main(){
    int m, k;
    while(cin >> m >> k){
        if(m == 0 && k == 0) break;
        int sum = m + m / k;//可以得到的总话费
        int extra = m / k + m % k;//额外话费包括:送的+充值时不够K元剩下的
        
        while(extra / k != 0){
            sum += extra / k;
            extra = extra / k + extra % k;
        }
        cout << sum << endl;
    }
}

简单来说就是:我现在有 M 元,但是因为每充 K 元就送1元话费,所以我要把 M 元尽量分 M/K 次来充,这样每次我就能额外获得 M/K 元

而无法除完的部分,就是剩余话费extra(送的+充值时不够K元剩下的)

然后我们拿剩的话费继续充值,还是以除K的方式充,每次能得到的额外话费就是 extra/K,加到总话费中,直到没有额外话费就结束

打印图形

打印数字图形

题目描述

先要求你从键盘输入一个整数n(1<=n<=9),打印出指定的数字图形。

输入

输入包含多组测试数据。每组输入一个整数n(1<=n<=9)。

输出

对于每组输入,输出指定的数字图形。
注意:每行最后一个数字后没有任何字符。

样例输入
5
样例输出
    1
   121
  12321
 1234321
123454321
 1234321
  12321
   121
    1
输入输出知识点

这里考察的是输出的技巧

像上面这种图形可以分成上下两部分进行打印

以上半部分为例,假设n = 3,应该怎么打印?

上半部分是由空格、递增的数、递减的数这三部分组成的,分别打印即可。打印时通过循环控制,优先考虑空格

先观察。n为3时,第一行的空格有2个,递增的数有一个,递减的没有;

以此类推,再通过两次for循环控制打印元素的个数(做的时候画图对照就知道该怎么设置循环初始值了)

注意,这只是打印,不要考虑指针会不会重叠,打印东西的时候只需要考虑打几个就行

代码
#include<iostream>

using namespace std;

void printTop(int n){
    for(int i = 1; i <= n; ++i){
        for(int j = 1; j <= n - i; ++j){//打印空格
            cout << " ";
        }
        
        for(int j = 1; j <= i; ++j){//打印递增数字
            cout << j;//递增肯定从小到大
        }
        
        for(int j = i - 1; j >= 1; --j){//打印递减数字
            cout << j;//递减肯定从大到小(基于此再去想怎么控制这个数以满足规则)
        }
        cout << endl;//记得换行
    }
}

void printDown(int n){//逻辑一样,只是i的初始值需要修改为从大到小
    for(int i = n - 1; i >= 1; --i){
        for(int j = 1; j <= n - i; ++j){
            cout << " ";
        }
        
        for(int j = 1; j <= i; ++j){
            cout << j;
        }
        
        for(int j = i - 1; j >= 1; --j){
            cout << j;
        }
        cout << endl;
    }
}

int main(){
    int n;
    while(cin >> n){
        if(n < 1 || n > 9) cout << "error" << endl;
        printTop(n);
        printDown(n);
    }
}

镂空三角形

题目描述

把一个字符三角形掏空,就能节省材料成本,减轻重量,但关键是为了追求另一种视觉效果。在设计的过程中,需要给出各种花纹的材料和大小尺寸的三角形样板,通过电脑临时做出来,以便看看效果。

输入

每行包含一个字符和一个整数n(0<n<41),不同的字符表示不同的花纹,整数n表示等腰三角形的高。显然其底边长为2n-1。如果遇到@字符,则表示所做出来的样板三角形已经够了。

输出

每个样板三角形之间应空上一行,三角形的中间为空。行末没有多余的空格。每条结果后需要再多输出一个空行。

样例输入
X 2
A 7
@
样例输出
 X
XXX

      A
     A A
    A   A
   A     A
  A       A
 A         A
AAAAAAAAAAAAA
输入输出知识点

“打印数字图形”的升级版

还是要遵循分别打印空格和字符的逻辑,如果不清楚要打印多少,就根据示例中每行空格和字符的个数来推导

做的时候记得画图

代码
#include<iostream>

using namespace std;

void printSpace(int n){
    for(int i = 0; i < n; ++i) cout << " ";
}

void printChar(int n, char a){
    for(int i = 0; i < n; ++i) cout << a;
}

int main(){
    char edge;
    int n;
    while(cin >> edge){
        if(edge == '@') break;
        cin >> n;
        if(n < 0 || n > 41) cout << "error" << endl;
        if(n == 1) cout << edge << endl;
        
        for(int i = 1; i <= n - 1; ++i){
            printSpace(n - i);//打印空格     
            if(i == 1){
                cout << edge;
            }else{
                cout << edge;
                printSpace(2 * i - 3);//打印中间的空格
                cout << edge;
            }
            cout << endl;
        }
        if(n > 1){//当n大于1就要打印次变底边
            printChar(2 * n - 1, edge);
            cout << endl;
        }
        cout << endl;
    }
}

处理n组数据输入

句子缩写

题目描述

输出一个词组中每个单词的首字母的大写组合。

输入

输入的第一行是一个整数n,表示一共有n组测试数据。(输入只有一个n,没有多组n的输入)
接下来有n行,每组测试数据占一行,每行有一个词组,每个词组由一个或多个单词组成;每组的单词个数不超过10个,每个单词有一个或多个大写或小写字母组成;
单词长度不超过10,由一个或多个空格分隔这些单词。

输出

请为每组测试数据输出规定的缩写,每组输出占一行。

样例输入
1
ad dfa     fgs
样例输出
ADF
提示

注意:单词之间可能有多个空格

输入输出知识点

1、演示了怎么把一个小写字符转换为大小

用ASCII码

2、演示了如何处理n组数据输入

在循环外用cin接收n,然后在while中n--

3、字符串输入后会带有一个回车,需要处理掉

用tmp = getchar();处理

代码
#include<iostream>

using namespace std;

char char2Capital(char& c){ // 小写变大写
    if(c >= 'a' && c <= 'z') c -= 32;
    return c;
}

int main(){
    int n;
    string res, s;
    cin >> n;
    
    char tmp;
    tmp = getchar(); // 吸收一个回车,因为输入n之后,要输入一个回车
    
    while(n--){//不能--n
        res = "";
        getline(cin, s);
        res += char2Capital(s[0]); 
        
        for(int i = 1; i < s.size() - 1; ++i){
            if(s[i] == ' ' && s[i + 1] != ' '){
                res += char2Capital(s[i + 1]); 
            }
        }
        cout << res << endl;
    }
}

神秘字符

题目描述

考古学家发现墓碑上有神秘的字符。经过仔细研究,发现原来这是开启古墓入口的方法。
墓碑上有2行字符串,其中第一个串的长度为偶数,现在要求把第2个串插入到第一个串的正中央,如此便能开启墓碑进入墓中。

输入

输入数据首先给出一个整数n,表示测试数据的组数。(整个输入中,只有一个n)
然后是n组数据,每组数据2行,每行一个字符串,长度大于0,小于50,并且第一个串的长度必为偶数。

输出

请为每组数据输出一个能开启古墓的字符串,每组输出占一行。

样例输入
2
asdf
yu
rtyu
HJK
样例输出
asyudf
rtHJKyu
输入输出知识点

1、在获取两个字符串输入时,需要使用一个char变量接收其后的回车键

具体就是

char tmp;
...
tmp = getchar();
代码
#include<iostream>
#include<string>

using namespace std;

int main(){
    int n;
    char tmp;
    cin >> n;
    string a, b;
    while(n--){
        cin >> a;
        tmp = getchar();
        cin >> b;
        string res = "";
        for(int i = 0; i < a.size() / 2; ++i){//遍历,将a的前半部分加到res中
            res += a[i];
        }
        res += b;
        
        for(int i = a.size() / 2; i < a.size(); ++i){//处理后半部分
            res += a[i];
        }
        cout << res << endl;
    }
}

位置互换

题目描述

给定一个长度为偶数位的字符串,请编程实现字符串的奇偶位互换。

输入

输入包含多组测试数据。
输入的第一行是一个整数n,表示有测试数据。(整个输入中,只有一个n)
接下来是n组测试数据,保证串长为偶数位(串长<=50)。

输出

请为每组测试数据输出奇偶位互换后的结果,每组输出占一行。

样例输入
2
0aa0
bb00
样例输出
a00a
bb00
代码
#include<iostream>
#include<string>

using namespace std;

void swap(char &a, char &b) { // 交换两个字符串
    char tmp = a;
    a = b;
    b = tmp;
}

int main(){
    int n;
    string s;
    cin >> n;
    while(n--){
        cin >> s;
        // for(int l = 0, r = s.size() - 1; l < r; l++, r--){//是相邻奇偶位交换
        //     swap(s[l], s[r]);
        // }
        for (int i = 0; i < s.size() - 1; i += 2) { // 在s字符串上原地修改
            swap(s[i], s[i + 1]);
        }
        cout << s << endl;
    }
}

出栈合法性

题目描述

已知自然数1,2,...,N(1<=N<=100)依次入栈,请问序列C1,C2,...,CN是否为合法的出栈序列。

输入

输入包含多组测试数据。
每组测试数据的第一行为整数N(1<=N<=100),当N=0时,输入结束。
第二行为N个正整数,以空格隔开,为出栈序列。

输出

对于每组输入,输出结果为一行字符串。
如给出的序列是合法的出栈序列,则输出Yes,否则输出No。

样例输入
5
3 4 2 1 5
5
3 5 1 4 2
0
样例输出
Yes
No

代码

使用一个vector接收输入的数组,方法跟前面的类似

创建一个栈,使用for将自然数1,2,...,N(1<=N<=100)模拟加入栈

同时比较当前的栈顶元素是否与vector中对应下标的一致,一致就弹出并移动索引

#include<iostream>
#include<vector>
#include<stack>

using namespace std;

int main(){
    int n;
    vector<int> nums(105, 0);
    while(cin >> n){
        if(n == 0) break;
        for(int i = 0; i < n; ++i) cin >> nums[i];
        
        int index = 0;
        stack<int> st;
        for(int i = 1; i <= n; ++i){
            st.push(i);
            while(!st.empty() && st.top() == nums[index]){
                st.pop();
                index++;
            }
        }
        if(st.empty() && index == n) cout << "Yes" <<endl;
        else cout << "No" <<endl;
    }
}

牛牛出入圈

描述

假设有一群牛要依次入圈和出圈,牛的编号是唯一的且不重复。给定两个牛的序列 enterleave,它们分别表示牛的入圈顺序和出圈顺序。判断是否存在一种操作顺序,使得牛按照给定的入圈和出圈顺序依次进入和离开圈子。如果存在这样的操作顺序,则返回 true,否则返回 false

注:牛圈狭长,只能先进入的牛必须等后进入的牛出圈后才能出圈。

示例1

输入:

[1, 2, 3, 4],[2, 1, 4, 3]

返回值:

true

代码

这题就是检查出入栈的合法性,不同的是,这这里要使用两个指针leaveIndex和enterIndex(从1开始,因为一开始需要先把enter的第一个元素加入栈中)来控制进出栈的顺序

先遍历入栈数组enter,将遍历元素压栈。过程中不断比较栈顶元素与leave中leaveIndex下标元素是否相等

若相等,弹出当前栈顶元素,移动leaveIndex指针;当leaveIndex等于leave数组长度时就返回true

若不相等,继续将enter中元素压栈,直到压完所有元素,循环结束;

此时,如果栈中还剩元素,那么将其依次出栈并与当前leave中leaveIndex下标元素比较,相等就弹出

如果有一个不相等即返回false;

当栈中元素弹空,则返回true

class Solution {
public:
    bool validateCowCircle(vector<int>& enter, vector<int>& leave) {
        // write code here
        stack<int> st;
        int leaveIndex = 0;
        st.push(enter[0]);
        int enterIndex = 1;

        while(true){//一直往栈中添加enter内的元素,并不断判断,如果当前栈顶元素等于leave中leaveIndex下标位置元素,弹出
            if(!st.empty() && st.top() == leave[leaveIndex]){
                st.pop();
                leaveIndex++;
                if(leaveIndex == leave.size()) return true;
            }else{//否则继续往栈里压入enter元素
                st.push(enter[enterIndex]);
                enterIndex++;
                if(enterIndex == enter.size()) break;
            }
        }
        //处理完压栈过程中的相同元素后,将栈中元素全部弹出,看看是否满足leave的顺序
        while(!st.empty()){
            if(st.top() == leave[leaveIndex]) {
                st.pop();
                leaveIndex++;
            }else{
                return false;
            }
        }
        return true;  
    }
};

ACM中链表的基本操作

输入

输入数据只有一组,第一行有n+1个整数,第一个整数是这行余下的整数数目n,后面是n个整数。

这一行整数是用来初始化列表的,并且输入的顺序与列表中的顺序相反,也就是说如果列表中是1、2、3那么输入的顺序是3、2、1。

第二行有一个整数m,代表下面还有m行。每行有一个字符串,字符串是“get”,“insert”,“delete”,“show”中的一种。

如果是“get”,代表获得第a个元素。(a从1开始计数)

如果是“delete”,代表删除第a个元素。(a从1开始计数)

如果是“insert”,则其后跟着两个整数a和e,代表在第a个位置前面插入e。(a从1开始计数)

“show”之后没有正数,直接打印链表全部内容。

输出

如果获取成功,则输出该元素;

如果删除成功,则输出“delete OK”;

如果获取失败,则输出“get fail”

如果删除失败,则输出“delete fail”

如果插入成功,则输出“insert OK”,否则输出“insert fail”。

如果是“show”,则输出列表中的所有元素,如果列表是空的,则输出“link list is empty”

注:所有的双引号均不输出。

样例输入
3 3 2 1
21
show
delete 1
show
delete 2
show
delete 1
show
delete 2
insert 2 5
show
insert 1 5
show
insert 1 7
show
insert 2 5
show
insert 3 6
show
insert 1 8
show
get 2
样例输出
1 2 3
delete OK
2 3
delete OK
2
delete OK
Link list is empty
delete fail
insert fail
Link list is empty
insert OK
5
insert OK
7 5
insert OK
7 5 5
insert OK
7 5 6 5
insert OK
8 7 5 6 5
7
提示

初始化链表的元素是倒序的,这个使用题目中创建列表的方法(从头部插入)就可以了。

代码

下面为标答,我自己写的时候虽然都写出来了,但是总是解答错误

主要思路就是定义链表节点,然后直接按照以前的做法,依次实现链表获取指定节点、头插、尾插、指定位置插入、删除指定节点即可,最后根据题目的具体题意再调用各函数来实现所需的功能

#include<iostream>
using namespace std;
// 定义链表节点结构体
struct LinkedNode {
    int val;
    LinkedNode* next;
    LinkedNode(int val):val(val), next(nullptr){}
};

int _size = 0;
LinkedNode* _dummyHead =  new LinkedNode(0); // 这里定义的头结点 是一个虚拟头结点,而不是真正的链表头结点

// 获取到第index个节点数值,如果index是非法数值直接返回-1, 注意index是从0开始的,第0个节点就是头结点
int get(int index) {
    if (index > (_size - 1) || index < 0) {
        return -1;
    }
    LinkedNode* cur = _dummyHead->next;
    while(index--){ // 如果--index 就会陷入死循环
        cur = cur->next;
    }
    return cur->val;
}

// 在链表最前面插入一个节点,插入完成后,新插入的节点为链表的新的头结点
void addAtHead(int val) {
    LinkedNode* newNode = new LinkedNode(val);
    newNode->next = _dummyHead->next;
    _dummyHead->next = newNode;
    _size++;
}

// 在链表最后面添加一个节点
void addAtTail(int val) {
    LinkedNode* newNode = new LinkedNode(val);
    LinkedNode* cur = _dummyHead;
    while(cur->next != nullptr){
        cur = cur->next;
    }
    cur->next = newNode;
    _size++;
}

// 在第index个节点之前插入一个新节点,例如index为0,那么新插入的节点为链表的新头节点。
// 如果index 等于链表的长度,则说明是新插入的节点为链表的尾结点
// 如果index大于链表的长度,则返回空
// 如果index小于0,则在头部插入节点
int addAtIndex(int index, int val) {

    if(index > _size) return -1;
    if(index < 0) return -1;
    LinkedNode* newNode = new LinkedNode(val);
    LinkedNode* cur = _dummyHead;
    while(index--) {
        cur = cur->next;
    }
    newNode->next = cur->next;
    cur->next = newNode;
    _size++;
    return 0;
}

// 删除第index个节点,如果index 大于等于链表的长度,直接return,注意index是从0开始的
int deleteAtIndex(int index) {
    if (index >= _size || index < 0) {
        return -1;
    }
    LinkedNode* cur = _dummyHead;
    while(index--) {
        cur = cur ->next;
    }
    LinkedNode* tmp = cur->next;
    cur->next = cur->next->next;
    delete tmp;
    //delete命令指示释放了tmp指针原本所指的那部分内存,
    //被delete后的指针tmp的值(地址)并非就是NULL,而是随机值。也就是被delete后,
    //如果不再加上一句tmp=nullptr,tmp会成为乱指的野指针
    //如果之后的程序不小心使用了tmp,会指向难以预想的内存空间
    tmp=nullptr;
    _size--;
    return 0;
}

// 打印链表
int printLinkedList() {
    LinkedNode* cur = _dummyHead;
    if (cur->next == nullptr) return -1;
    while (cur->next != nullptr) {
        cout << cur->next->val << " ";
        cur = cur->next;
    }
    cout << endl;
    return 0;
}

int main() {
    int n, a, m, t, z;
    string s;
    cin >> n;
    LinkedNode* head = nullptr;
    while (n--) {
        cin >> a;
        addAtHead(a);
    }
    cin >> m;
    while (m--) {
        cin >> s;
        if (s == "show")  {
            if (printLinkedList() == -1) cout << "Link list is empty" << endl;
        }
        if (s == "delete") {
            cin >> t; 
            // 本题的删除索引是从1开始,函数实现是从0开始,所以这里是 t - 1
            if (deleteAtIndex(t - 1) == -1) cout << "delete fail" << endl; 
            else cout << "delete OK" << endl;
        }
        if (s == "insert") {
            cin >> t >> z; 
            if (addAtIndex(t - 1, z) == -1) cout << "insert fail" << endl;
            else cout << "insert OK" << endl;

        }
        if (s == "get") {
            cin >> t;
            int getValue = get(t - 1);
            if (getValue == -1) cout << "get fail" << endl;
            else cout << getValue << endl;
        }
    }
}

构造单链表并反转

题目描述

根据一个整数序列构造一个单链表,然后将其反转。

例如:原单链表为 2 3 4 5 ,反转之后为5 4 3 2

输入

输入包括多组测试数据,每组测试数据占一行,第一个为大于等于0的整数n,表示该单链表的长度,后面跟着n个整数,表示链表的每一个元素。整数之间用空格隔开

输出

针对每组测试数据,输出包括两行,分别是反转前和反转后的链表元素,用空格隔开

如果链表为空,则只输出一行,list is empty

样例输入
5 1 2 3 4 5 
0
样例输出
1 2 3 4 5 
5 4 3 2 1 
list is empty
提示

本题用数组,也是可有过的,输入一遍数组,然后倒叙输出。不过建议大家用本题来链表操作

代码

技巧点在于,如何处理输入的链表数据,如何存放

实际上处理这种输入我们只需要两个变量就可以了,一个是n,用于判断我们应该接收多少个节点值;另一个是nodeVal,用于循环接收输入的节点值,并直接用来构造链表

#include<iostream>

using namespace std;
struct ListNode{
    int val;
    ListNode* next;
    ListNode(int val): val(val), next(nullptr){}
};

ListNode* reverseList(ListNode* head){//双指针法
    ListNode* pre = nullptr;
    ListNode* cur = head;  
    while(cur){
        ListNode* tmp = cur->next;
    
        cur->next = pre;
        pre = cur;
        cur = tmp;
    }   
    return pre;
}

void printListNode(ListNode* head){
    ListNode* cur = head;
    while(cur != nullptr){
        cout << cur->val << ' ';
        cur = cur->next;
    }
    cout << endl;
}

int main(){
    int n, nodeVal;
    ListNode* dummy = new ListNode(0);
    while(cin >> n){
        if (n == 0) {
                cout << "list is empty" << endl;
                continue;
        }
        ListNode* cur = dummy;
        while(n--){
            cin >> nodeVal;
            ListNode* node4add = new ListNode(nodeVal);
            cur->next = node4add;
            cur = cur->next;
        }
        printListNode(dummy->next);
        printListNode(reverseList(dummy->next));
    }
}

删除单链表中的重复数据

题目描述

根据一个递增的整数序列构造有序单链表,删除其中的重复元素

输入

输入包括多组测试数据,每组测试数据占一行,第一个为大于等于0的整数n,表示该单链表的长度,后面跟着n个整数,表示链表的每一个元素。整数之间用空格隔开

输出

针对每组测试数据,输出包括两行,分别是删除前和删除后的链表元素,用空格隔开

如果链表为空,则只输出一行,list is empty

样例输入
5 1 2 3 4 5
5 1 1 2 2 3
0
样例输出
1 2 3 4 5 
1 2 3 4 5 
1 1 2 2 3 
1 2 3 
list is empty

代码

题目只是要一个输出,所以没有必要真的去删除某个节点,只需要在打印时跳过就行

#include<iostream>

using namespace std;
struct ListNode{
    int val;
    ListNode* next;
    ListNode(int val): val(val), next(nullptr){}
};

// ListNode* removeDuplicate(ListNode* head){
//     ListNode* cur = head;
//     while(cur){
//         ListNode* tmp = cur;
//         cur = cur->next;
//         if(cur->val == tmp->val){
//             cur = tmp;
//             cur->next = cur->next->next;
//         }
//     }
//     return head;
// }

// 过滤掉重复元素输出,不用真的删除
void removeDuplicate(ListNode* head) {
    ListNode* cur = head;
    while (cur != nullptr && cur->next != nullptr) { 
        if (cur->val != cur->next->val) cout << cur->val << " ";
        cur = cur->next;
    }
    cout << cur->val << " ";//最后一个节点不满足while没被输出,这里补上
    cout << endl;
}

void printListNode(ListNode* head){
    ListNode* cur = head;
    while(cur){
        cout << cur->val << " ";
        cur = cur->next;
    }
    cout << endl;
}

int main(){
    int n, nodeVal;
    ListNode* dummy = new ListNode(0);
    while(cin >> n){
        if (n == 0) {
                cout << "list is empty" << endl;
                continue;
        }
        ListNode* cur = dummy;
        while(n--){
            cin >> nodeVal;
            ListNode* node4add = new ListNode(nodeVal);
            cur->next = node4add;
            cur = cur->next;
        }
        printListNode(dummy->next);
        //去重
        removeDuplicate(dummy->next);
    }
}

ACM构造二叉树

题目描述

给你一棵二叉树的前序遍历和中序遍历结果,要求你写出这棵二叉树的后序遍历结果。

输入

输入包含多组测试数据。每组输入包含两个字符串,分别表示二叉树的前序遍历和中序遍历结果。每个字符串由不重复的大写字母组成。

输出

对于每组输入,输出对应的二叉树的后续遍历结果。

样例输入
DBACEGF ABCDEFG
BCAD CBAD
样例输出
ACBFGED
CDAB

代码

#include <iostream>
#include <unordered_map>
#include <string>
using namespace std;

// 定义二叉树节点结构
struct TreeNode {
    char val;
    TreeNode* left;
    TreeNode* right;
    TreeNode(int val) : val(val), left(nullptr), right(nullptr) {}
};

TreeNode* buildTreeRecursive(string& preorder, string& inorder,
                             int preStart, int inStart, int inEnd,
                             unordered_map<int, int>& indexMap) {
    if (preStart > preorder.size() - 1 || inStart > inEnd) {
        return nullptr;
    }

    // 根据前序遍历结果获取当前子树的根节点值
    char rootValue = preorder[preStart];
    TreeNode* root = new TreeNode(rootValue);

    // 根据中序遍历结果获取当前子树的根节点在中序遍历中的索引
    int rootIndex = indexMap[rootValue];

    // 递归构造左子树和右子树
    root->left = buildTreeRecursive(preorder, inorder, preStart + 1, inStart, rootIndex - 1, indexMap);
    root->right = buildTreeRecursive(preorder, inorder, preStart + rootIndex - inStart + 1, rootIndex + 1, inEnd, indexMap);

    return root;
}

// 根据前序遍历和中序遍历结果构造二叉树
TreeNode* buildTree(string& preorder, string& inorder) {
    // 创建一个哈希表,用于快速查找中序遍历结果中节点值对应的索引
    std::unordered_map<int, int> indexMap;
    for (int i = 0; i < inorder.size(); ++i) {
        indexMap[inorder[i]] = i;
    }

    // 递归构造二叉树
    return buildTreeRecursive(preorder, inorder, 0, 0, inorder.size() - 1, indexMap);
}
// 后序遍历二叉树
void postorderTraversal(TreeNode* root) {
    if (root == nullptr) {
        return;
    }

    postorderTraversal(root->left);
    postorderTraversal(root->right);
    cout << root->val;
}

int main() {

    string s;
    while (getline(cin, s)) { // 接受一整行字符串
        string preorder = "", inorder = "";
        // 拆分出两个字符串
        int i;
        for (i = 0; s[i] != ' '; i++) preorder += s[i];  
        i++;
        for (; i < s.size(); i++) inorder += s[i]; 

        // 开始构造二叉树
        TreeNode* root = buildTree(preorder, inorder);
        postorderTraversal(root);
        cout << endl;
    }
    return 0;
}

ACM遍历二叉树

代码

#include <iostream>
#include <unordered_map>
#include <vector>
using namespace std;

struct TreeNode {
    char val;
    TreeNode* left;
    TreeNode* right;
    TreeNode(char val) : val(val), left(nullptr), right(nullptr) {}
};

// 根据输入数据构造二叉树
TreeNode* buildTree(unordered_map<char, pair<char, char>>& nodeMap, char rootValue) {
    if (rootValue == '0') {
        return nullptr;
    }

    TreeNode* root = new TreeNode(rootValue);
    int leftChild = nodeMap[rootValue].first;
    int rightChild = nodeMap[rootValue].second;

    root->left = buildTree(nodeMap, leftChild);
    root->right = buildTree(nodeMap, rightChild);

    return root;
}

// 前序遍历二叉树
void preorderTraversal(TreeNode* root) {
    if (root == nullptr) {
        return;
    }

    cout << root->val;
    preorderTraversal(root->left);
    preorderTraversal(root->right);
}

// 中序遍历二叉树
void inorderTraversal(TreeNode* root) {
    if (root == nullptr) {
        return;
    }

    inorderTraversal(root->left);
    cout << root->val;
    inorderTraversal(root->right);
}

// 后序遍历二叉树
void postorderTraversal(TreeNode* root) {
    if (root == nullptr) {
        return;
    }

    postorderTraversal(root->left);
    postorderTraversal(root->right);
    cout << root->val;
}

int main() {
    int n;
    cin >> n;
    unordered_map<char, std::pair<char, char>> nodeMap;

    // 先保存输入的数据 
    vector<char> index = vector<char>(n + 1, '0'); 
    vector<vector<int>> nums = vector<vector<int>>(n + 1, vector<int>(2, 0));
    for (int i = 1; i <= n; i++) {
        cin >> index[i] >> nums[i][0] >> nums[i][1];
    }

    // 生成二叉树 
    for (int i = 1; i <= n; i++) {
        nodeMap[index[i]] = make_pair(index[nums[i][0]], index[nums[i][1]]);
    }
    TreeNode* root = buildTree(nodeMap, index[1]);

    preorderTraversal(root);
    cout << std::endl;

    inorderTraversal(root);
    cout << std::endl;

    postorderTraversal(root);
    cout << std::endl;

    return 0;
}

ACM二叉树的高度

代码

#include <iostream>
#include <string>
#include <unordered_map>
using namespace std;

struct TreeNode {
    char val;
    TreeNode* left;
    TreeNode* right;
    TreeNode(char val) : val(val), left(nullptr), right(nullptr) {}
};

// 根据先序遍历和中序遍历序列构造二叉树
TreeNode* buildTree(string& preorder, string& inorder,
                    int preStart, int inStart, int inEnd,
                    unordered_map<char, int>& indexMap) {
    if (preStart > preorder.size() - 1 || inStart > inEnd) {
        return nullptr;
    }

    char rootValue = preorder[preStart];
    TreeNode* root = new TreeNode(rootValue);
    int rootIndex = indexMap[rootValue];

    root->left = buildTree(preorder, inorder, preStart + 1, inStart, rootIndex - 1, indexMap);
    root->right = buildTree(preorder, inorder, preStart + rootIndex - inStart + 1, rootIndex + 1, inEnd, indexMap);

    return root;
}

// 计算二叉树的高度
int getHeight(TreeNode* root) {
    if (root == nullptr) {
        return 0;
    }

    int leftHeight = getHeight(root->left);
    int rightHeight = getHeight(root->right);

    return max(leftHeight, rightHeight) + 1;
}

int main() {
    int n;
    while (cin >> n) {
        string preorder, inorder;
        cin >> preorder >> inorder;

        unordered_map<char, int> indexMap;
        for (int i = 0; i < n; ++i) {
            indexMap[inorder[i]] = i;
        }

        TreeNode* root = buildTree(preorder, inorder, 0, 0, n - 1, indexMap);
        int height = getHeight(root);

        cout << height << endl;
    }
    return 0;
}

最长公共子序列

代码

#include <iostream>
#include <vector>
#include <string>
using namespace std;
int main() {
    string text1, text2;
    while (cin >> text1 >> text2) {
         vector<vector<int>> dp(text1.size() + 1, vector<int>(text2.size() + 1, 0));
        for (int i = 1; i <= text1.size(); i++) {
            for (int j = 1; j <= text2.size(); j++) {
                if (text1[i - 1] == text2[j - 1]) {
                    dp[i][j] = dp[i - 1][j - 1] + 1;
                } else {
                    dp[i][j] = max(dp[i - 1][j], dp[i][j - 1]);
                }
            }
        }
        cout << dp[text1.size()][text2.size()] << endl;
    }
    return 0;
}