这是浙江大学《数据结构》刷题记录的第二篇,越发觉得编程是一件很有意思的事情。而且对于EE/CS/AI专业的学生,多刷算法题和数据结构题真的对于无论找工还是培养工程代码能力都大有裨益。下面就具体论述下这次的五道数据结构题目。

二分查找

链接:https://pintia.cn/problem-sets/1497448825169559552/problems/1497448917745405953
这题就是最最原始的二分查找,之后刷很多算法题都有可能用到这个算法,因为其复杂度只有CS专题——浙大数据结构刷题(二) - 图1。但是要注意的一点是,二分查找需要数组或者序列是排好序的有序数列
代码如下:

  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. #define MAXSIZE 10
  4. #define NotFound 0
  5. typedef int ElementType;
  6. typedef int Position;
  7. typedef struct LNode *List;
  8. struct LNode {
  9. ElementType Data[MAXSIZE];
  10. Position Last; /* 保存线性表中最后一个元素的位置 */
  11. };
  12. List ReadInput(); /* 裁判实现,细节不表。元素从下标1开始存储 */
  13. Position BinarySearch( List L, ElementType X );
  14. int main()
  15. {
  16. List L;
  17. ElementType X;
  18. Position P;
  19. L = ReadInput();
  20. scanf("%d", &X);
  21. P = BinarySearch( L, X );
  22. printf("%d\n", P);
  23. return 0;
  24. }
  25. /* 你的代码将被嵌在这里 */
  26. Position BinarySearch( List L, ElementType X ){
  27. Position end = L->Last;
  28. Position start = 1;
  29. Position temp;
  30. Position i = NotFound;
  31. while(1){
  32. if(end==start){
  33. if(X==L->Data[end])
  34. return end;
  35. else
  36. break;
  37. }
  38. if((end-start+1)%2==0){
  39. temp = (end-start+1)/2 + start - 1;
  40. if(X==L->Data[temp])
  41. return temp;
  42. else if(X==L->Data[temp+1])
  43. return temp+1;
  44. else if(X<L->Data[temp])
  45. end = temp;
  46. else
  47. start = temp + 1;
  48. }
  49. else{
  50. temp = (start+end)/2;
  51. if(X==L->Data[temp])
  52. return temp;
  53. else if(X<L->Data[temp])
  54. end = temp - 1;
  55. else
  56. start = temp + 1;
  57. }
  58. }
  59. return i;
  60. }

树的同构问题

链接:https://pintia.cn/problem-sets/1497448825169559552/problems/1503334324290916352
其实在解决树的很多问题我们都需要用到一个很重要的性质——那就是树的递归效应,即树的子树仍旧是一棵树,所以在树的许多算法中也会用到递归的算法,在程序中经常体现为函数的递归调用,当然在递归时要经常考虑是否还能继续递归,即还有没有左节点或者右节点(节点可能为空,那就不需要继续递归了)。
代码如下:

  1. #include<iostream>
  2. #include<cstdlib>
  3. #include<cstdio>
  4. #define Maxsize 10
  5. using namespace std;
  6. class Node{
  7. public:
  8. char value;
  9. int left;
  10. int right;
  11. Node(){
  12. value = 'a';
  13. left = -1;
  14. right = -1;
  15. }
  16. };
  17. Node tree[Maxsize];
  18. int check[Maxsize] = {0};
  19. Node tree2[Maxsize];
  20. int check2[Maxsize] = {0};
  21. void Issame(int root,int root2);
  22. int Isthesame = 1;
  23. int main(){
  24. int N,root;
  25. char tempchar;
  26. //读入数据并建树1
  27. cin>>N;
  28. for(int i = 0;i < N;i++){
  29. cin >> tree[i].value;
  30. cin >> tempchar;
  31. if(tempchar != '-'){
  32. tree[i].left = int(tempchar) - 48;
  33. check[(int(tempchar) - 48)] = 1;
  34. }
  35. else
  36. tree[i].left = -1;
  37. cin >> tempchar;
  38. if(tempchar != '-'){
  39. tree[i].right = int(tempchar) - 48;
  40. check[(int(tempchar) - 48)] = 1;
  41. }
  42. else
  43. tree[i].right = -1;
  44. }
  45. //寻找根节点
  46. for(int i = 0;i < N;i++)
  47. {
  48. if(check[i] == 0){
  49. root = i;
  50. break;
  51. }
  52. }
  53. //for(int i = 0;i < N;i++)
  54. // cout<<tree[i].value<<" "<<tree[i].left<<" "<<tree[i].right<<endl;
  55. //读入数据并建树2
  56. int N2,root2;
  57. cin>>N2;
  58. for(int i = 0;i < N2;i++){
  59. cin >> tree2[i].value;
  60. cin >> tempchar;
  61. if(tempchar != '-'){
  62. tree2[i].left = int(tempchar) - 48;
  63. check2[(int(tempchar) - 48)] = 1;
  64. }
  65. else
  66. tree2[i].left = -1;
  67. cin >> tempchar;
  68. if(tempchar != '-'){
  69. tree2[i].right = int(tempchar) - 48;
  70. check2[(int(tempchar) - 48)] = 1;
  71. }
  72. else
  73. tree2[i].right = -1;
  74. }
  75. for(int i = 0;i < N2;i++)
  76. {
  77. if(check2[i] == 0){
  78. root2 = i;
  79. break;
  80. }
  81. }
  82. //for(int i = 0;i < N2;i++)
  83. // cout<<tree2[i].value<<" "<<tree2[i].left<<" "<<tree2[i].right<<endl;
  84. //cout<<root<<endl;
  85. //cout<<root2<<endl;
  86. //判断两棵树是否同构
  87. if((N == 0 && N2 != 0)||(N != 0 && N2 == 0)){
  88. cout<<"No"<<endl;
  89. return 0;
  90. }
  91. else if(N == 0 && N2 == 0){
  92. cout<<"Yes"<<endl;
  93. return 0;
  94. }
  95. else
  96. Issame(root,root2);
  97. if(Isthesame == 1)
  98. cout<<"Yes"<<endl;
  99. else
  100. cout<<"No"<<endl;
  101. return 0;
  102. }
  103. void Issame(int root,int root2){
  104. //cout<<"1"<<endl;
  105. //考虑不同情况,采用递归的方法
  106. if(Isthesame == 0)
  107. return;
  108. if(tree[root].value != tree2[root2].value){
  109. Isthesame = 0;
  110. return;
  111. }
  112. else if((tree[root].left + tree[root].right == -2)&&(tree2[root2].left + tree2[root2].right == -2)){
  113. return;
  114. }
  115. else if((tree[root].left >= 0 && tree[root].right == -1)&&(tree2[root2].left >= 0 && tree2[root2].right == -1)){
  116. Issame(tree[root].left,tree2[root2].left);
  117. }
  118. else if((tree[root].left >= 0 && tree[root].right == -1)&&(tree2[root2].left == -1 && tree2[root2].right >= 0)){
  119. Issame(tree[root].left,tree2[root2].right);
  120. }
  121. else if((tree[root].left == -1 && tree[root].right >= 0)&&(tree2[root2].left >= 0 && tree2[root2].right == -1)){
  122. Issame(tree[root].right,tree2[root2].left);
  123. }
  124. else if((tree[root].left == -1 && tree[root].right >= 0)&&(tree2[root2].left == -1 && tree2[root2].right >= 0)){
  125. Issame(tree[root].right,tree2[root2].right);
  126. }
  127. else{
  128. if(tree[tree[root].left].value == tree2[tree2[root2].left].value){
  129. Issame(tree[root].left,tree2[root2].left);
  130. if(Isthesame != 0)
  131. Issame(tree[root].right,tree2[root2].right);
  132. }
  133. else{
  134. Issame(tree[root].left,tree2[root2].right);
  135. if(Isthesame != 0)
  136. Issame(tree[root].right,tree2[root2].left);
  137. }
  138. }
  139. }

List Leaves问题

链接:https://pintia.cn/problem-sets/1497448825169559552/problems/1503334324290916353
这题本质上就是使用层序遍历+判断叶节点,从而将叶节点一个个的输出。当谈到层序遍历时,我们需要使用队列queue来实现层序遍历,这里我使用了两个堆栈来代替队列执行一样的功能。
代码如下:

#include<iostream>
#include<cstdlib>
#include<cstdio>
#include<vector>
#define Maxsize 10

using namespace std;

class Node{
    public:
    int left;
    int right;
    Node(){
        left = -1;
        right = -1;
    }
};

Node tree[Maxsize];
int check[Maxsize] = {0};
int count = 0;

int main(){
    int N,root;
    char tempchar;
    //读入数据并建树1
    cin>>N;
    for(int i = 0;i < N;i++){
        cin >> tempchar;
        if(tempchar != '-'){
            tree[i].left = int(tempchar) - 48;
            check[(int(tempchar) - 48)] = 1;
        }
        else
            tree[i].left = -1;
        cin >> tempchar;
        if(tempchar != '-'){
            tree[i].right = int(tempchar) - 48;
            check[(int(tempchar) - 48)] = 1;
        }
        else
            tree[i].right = -1;
    }
    //寻找根节点
    for(int i = 0;i < N;i++)
    {
        if(check[i] == 0){
            root = i;
            break;
        }
    }
    //for(int i = 0;i < N;i++)
    //    cout<<i<<" "<<tree[i].left<<" "<<tree[i].right<<endl;
    //cout<<root<<endl;
    vector<int> stack1,stack2;
    int tempindex;
    //cout<<root;
    if(N == 0)
        return 0;
    else if(N == 1){
        cout<<root;
        return 0;
    }
    while(true){
        //pop阶段
        //下面使用两个stack来完成一个queue的功能
        if(stack1.size() != 0){
            auto begin = stack1.begin();
            auto end = stack1.end();
            while(end != begin){
                stack2.push_back(*(end-1));
                --end;
            }
            stack1.clear();
            tempindex = stack2.back();
            if(tree[tempindex].left == -1 && tree[tempindex].right == -1){
                if(count != 0){
                    cout<<" ";
                }
                cout<<tempindex;
                count++;
            }
            stack2.pop_back();
        }
        else{
            tempindex = root;
        }

        //push阶段
        if(stack2.size() != 0){
            auto begin2 = stack2.begin();
            auto end2 = stack2.end();
            while(end2 != begin2){
                stack1.push_back(*(end2-1));
                --end2;
            }
            stack2.clear();
        }

        if(tree[tempindex].left != -1){
            stack1.push_back(tree[tempindex].left);
            //cout<<tree[tempindex].left;
        }
        if(tree[tempindex].right != -1){
            stack1.push_back(tree[tempindex].right);
            //cout<<tree[tempindex].right;
        }

        //判断是否全部pop出
        if(stack1.size() == 0)
            break;
    }
    return 0;
}

Tree Traversals Again

链接:https://pintia.cn/problem-sets/1497448825169559552/problems/1503334324290916354
好难,研究ing。。
代码如下:

#include<iostream>
#include<cstdio>
#include<vector>
#include<string>
#define Maxsize 30

using namespace std;

class Node{
    public:
    int lastnode;
    int leftnode;
    int rightnode;
    int flag;
    Node(){
        leftnode = -1;
        rightnode = -1;
        flag = 0;
    }
};

Node array[Maxsize];

void Postorder(int index);

int main(){
    string No;
    int N;
    int count;
    count = 0;
    getline(cin, No, '\n');
    N = stoi(No);
    string temp,temp2;
    int num,root;
    int nowindex = 0;
    //根据operations of stack建树
    for(int p = 0;p < 2*N;p++){
        getline(cin, temp, '\n');
        if(array[root].flag == 1 && array[root].leftnode != -1 && array[root].rightnode != -1)
            goto f;
        //cout<<temp<<endl;
        //根节点及根节点左边
        if(p == 0){
            num = (int)(temp.back()) - 48;
            //cout<<num<<endl;
            root = num;
            count++;
            nowindex = num;
            array[nowindex].lastnode = -1;
            //cout<<"PUSH"<<endl;
        }
        else{
            if(temp.length() > 3){
                //cout<<"PUSH"<<endl;
                num = (int)(temp.back()) - 48;
                //cout<<num<<endl;
                if(array[nowindex].leftnode == -1 && array[nowindex].flag == 0)
                    array[nowindex].leftnode = num;
                else
                    array[nowindex].rightnode = num;
                array[num].lastnode = nowindex;
                nowindex = num;
                count++;
            }
            else{
                //cout<<"POP"<<endl;
                if(array[nowindex].leftnode == -1){
                    array[nowindex].flag = 1;
                    nowindex = array[nowindex].lastnode;
                    while(array[nowindex].flag == 1)
                        nowindex = array[nowindex].lastnode;

                }
                else{
                    array[nowindex].flag = 1;
                }
            }
        }
        continue;
        //根节点右边
        f:if(temp.length() > 3){
            num = (int)(temp.back()) - 48;
            //cout<<num<<endl;
            if(array[nowindex].leftnode == -1 && array[nowindex].flag == 0)
                array[nowindex].leftnode = num;
            else
                array[nowindex].rightnode = num;
            array[num].lastnode = nowindex;
            nowindex = num;
            count++;
        }
        else{

        }
        //cin>>temp;

        //for(int i = 0;i < count; i++)
        //    cout<<array[i].lastnode<<" "<<array[i].leftnode<<" "<<array[i].rightnode<<" "<<array[i].flag<<endl;
        //cout<<endl;
    }
    //for(int i = 0;i < count; i++)
    //    cout<<array[i].lastnode<<" "<<array[i].leftnode<<" "<<array[i].rightnode<<" "<<array[i].flag<<endl;

    //根据树进行后序遍历并输出
    Postorder(root);
    return 0;
}

int counts = 0;

void Postorder(int index){
    if(array[index].leftnode != -1)
        Postorder(array[index].leftnode);
    if(array[index].rightnode != -1)
        Postorder(array[index].rightnode);
    if(counts == 0){
        cout<<index;
        counts++;
    }
    else{
        cout<<" "<<index;
    }
}

同一棵二叉搜索树

链接:https://pintia.cn/problem-sets/1497448825169559552/problems/1505905647748263937
这题也是花了40分钟左右就AC的一道题,真的不难,还是利用到上面所说的“树的递归效应”将树根据根节点一分为二,然后将同样的操作作用于子树,递归判断子树,子树的子树等等。
代码如下:

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<vector>
#define Maxsize 10

using namespace std;

int flag = 1;
void Compare(int* a, int* b, int length1, int length2);

int main(){
    int N,L;
    int temps,tempnum;
    int arr_o[Maxsize];
    int arr_c[Maxsize];
    while(true){
        cin>>N;
        if(N == 0)
            return 0;
        cin>>L;
        //建立原始序列
        for(int i = 0;i < N;i++){
            cin >> tempnum;
            arr_o[i] = tempnum;
        }
        //读入与原始序列比较的L个序列
        for(int p = 0;p < L;p++){
            for(int i = 0;i < N;i++){
                cin >> temps;
                arr_c[i] = temps;
                if(temps == 0)
                    return 0;
            }
            flag = 1;
            //将两个序列进行比较
            Compare(arr_o,arr_c,N,N);
            if(flag == 0)
                cout<<"No"<<endl;
            else
                cout<<"Yes"<<endl;
        }
    }
    return 0;
}

void Compare(int* a, int* b, int length1, int length2){
    if(flag == 0)
        return;
    //长度不等直接返回No
    if(length1 != length2){
        //cout<<"a"<<endl;
        flag = 0;
        return;
    }
    //序列都为空直接返回
    if(length1 == 0 && length2 == 0){
        return;
    }
    else if(length1 <= 2){
        for(int i = 0;i < length1;i++){
            //根节点不一样直接No
            if(a[i] != b[i]){
                //cout<<"b"<<endl;
                flag = 0;
                return;
            }
        }
        return;
    }
    else{
        //如果根节点一样就直接比较根节点两边(大于和小于)的子序列,递归比较
        int tempa[Maxsize],tempb[Maxsize],tempa_2[Maxsize],tempb_2[Maxsize];
        int templen1,templen2,templen1_2,templen2_2;
        templen1 = templen2 = templen1_2 = templen2_2 = 0;
        for(int i = 0;i < length1;i++){
            if(i == 0){
                if(a[0] != b[0]){
                    //cout<<"c"<<endl;
                    flag = 0;
                    return;
                }
            }
            else{
                if(a[i] > a[0])
                    tempa_2[templen1_2++] = a[i];
                else
                    tempa[templen1++] = a[i];
                if(b[i] > b[0])
                    tempb_2[templen2_2++] = b[i];
                else
                    tempb[templen2++] = b[i];
            }
        }
        //递归比较左右子序列
        Compare(tempa,tempb,templen1,templen2);
        Compare(tempa_2,tempb_2,templen1_2,templen2_2);
        return;
    }
}

总结

从这次的五道题中,我又有新的收获,就是:1)关于代码的调试,可以多采用cout来观察想观察的变量来判断程序地bug在哪个地方;2)抓住问题的本质,多总结归纳,比如说树的递归效应,这在很多树的相关问题中都有用到;3)注释一定要及时补充好,不然真的过段时间非常容易忘记;4)写代码的时候多注意边界测试,比如i=0或者1,或者某个指针为空的情况等等。
欢迎对ECE/CS/AI感兴趣的小伙伴关注我,如果你对我的内容有什么建议的话,或者你也对算法和数据结构感兴趣的话,可以单独找我讨论,也欢迎在评论区留下你的声音。