[算法周训 1]二叉树与链表训练1

本周博主与朋友一起在LeetCode上进行了算法刷题,重点练习了二叉树和栈相关的题目。文章详细介绍了合并两个有序链表、路径总和、对称二叉树、相交链表、两数相加等经典问题的解题思路,包括递归、循环和栈的应用。此外,还强调了语言选择(推荐C++)和刷题的重要性,提倡通过多刷题来提升算法能力和代码熟练度。
摘要由CSDN通过智能技术生成

本周和大佬同学进行了一起的算法刷题,主要平台就是Leetcode,题型大致分为二叉树和栈。同时我自己也对栈和队列进行了相关的复习。
对于下面的这些题目,有必要写一点刷题总结(详见总结部分)。在此之前我也发了一些关于算法的一些见解,不过这个专栏主要是记录算法周训。

一般来说,刷题可以使用C++,Java,Go。不太推荐使用Python来刷题,因为很多功能也都封装好了。本人习惯于C++刷题,虽然近些时候Java也接触得蛮多,但是对于高校生以C++为主是趋势。前不久,高中NOI联赛很多地区也只能用C++,更不用说考研数据结构部分和复试上机部分也都是指定使用C/C++。当然了,语言并不是问题,关键是思维性,非常重要!

接下来,每周仍会以算法周训为导向,继续加强自己的Code与Debug能力。
以下的题目我也做了几遍,首先是每天的训练,再到今天的博客编写又重新码了一遍。第二次刷的时候思路便清晰了很多,码的速度也变快了不少。所以,掌握题目的最好的办法就是多练多刷几遍!

算法是基本功,掌握算法能力与代码编写能力才会让你更上一层楼!希望共同学习与交流进步!

合并两个有序链表

在这里插入图片描述
这个题目本质就是合并链表,需要注意的就是这个链表已经是有序的了。
使用递归注意递归的条件与参数。

ListNode* mergeTwoLists(ListNode * l1,ListNode *l2) {
    if(l1 == NULL) return l2;
    if(l2 == NULL) return l1;
    if(l1->val <= l2->val) {
        l1->next = mergeTwoLists(l1->next,l2);
        return l1;
    }else {
        l2->next = mergeTwoLists(l1,l2->next);
        return l2;
    }
  }

当然此题也可以使用循环归并排序,个人感觉也是相当不错的:类似先循环2个都是非空链表,然后进行插入。这里先构造一个头结点为0的head节点,然后通过cur指向并完成插入。这是比较常见的思路。

ListNode* mergeTwoLists(ListNode * l1,ListNode *l2) {
    ListNode *head = new ListNode(0);
    ListNode *cur = head;
    while( l1 && l2) {
        if(l1->val < l2->val) {
            cur->next = l1;
            cur = cur->next;
            l1 = l1->next;
        }else {
            cur->next = l2;
            cur = cur->next;
            l2 = l2->next;
        }
    }

    if(l1 == NULL) cur->next = l2;
    else cur->next = l1;
    return head->next;
    }

路径总和

在这里插入图片描述
这是一个关于二叉树的题目,比较常见,主流思路必定就是递归了,但是该如何递归呢?
首先考虑递归左树,如果没有得到target,那么就得-1然后递归右树。

bool hasPathSum(TreeNode* root, int sum) {
       if(root == nullptr) return false;
       if(root->left== nullptr && root->right == nullptr) {
           return sum == root->val; 
       }
       return hasPathSum(root->left,sum-root->val) || hasPathSum(root->right,sum-root->val);
    }

当然也可以使用dfs深搜,并进行相应的剪枝操作

	bool res=false ;
    void  dfs(TreeNode *node,int sum) {
        if(!node ) {
            return;
        }
        if(node->left == nullptr && node->right == nullptr) {
            if(sum == node->val) res = true;
        }
        if(!res) dfs(node->left,sum-node->val);
        if(!res) dfs(node->right,sum-node->val);
    }


    bool hasPathSum(TreeNode* root, int sum) {
      if(!root) return res;
      dfs(root,sum);
      return res;
    }

对称二叉树

在这里插入图片描述
这个题目就比较简单了,常见的从小到大(小问题-大问题)递归的模板。考虑一个子树是否对称然后考虑全体树是否对称。这里需要的是,一个指针从左边开始递归,另一个指针从右边开始递归。

bool fun(TreeNode *root1,TreeNode *root2) {
        if( root1==nullptr && root2 == nullptr) return true;
        if(root1 == nullptr || root2==nullptr) return false;
        return root1->val == root2->val && fun(root1->left,root2->right) && fun(root1->right ,root2->left);
    }     

    bool isSymmetric(TreeNode* root) {
       return fun(root,root);
    }

或者使用队列来满足条件:

bool isSymmetric(TreeNode* root) {
        if(!root) return true;
        queue<TreeNode *> que;
        que.push(root->left);
        que.push(root->right);
        while(!que.empty()) {
            TreeNode *leftnode = que.front();que.pop();
            TreeNode *rightnode = que.front();que.pop();
            if(!leftnode && !rightnode) return true;
            if(leftnode->val != rightnode->val || !leftnode || !rightnode) return false;
            que.push(leftnode->left);
            que.push(rightnode->right);
            que.push(leftnode->right);
            que.push(rightnode->left);
        }
        return true;
    }

相交链表

在这里插入图片描述
简单来说,这就是找内存地址一样的节点,注意不是节点中的值
很明显,遍历便可以解决。

ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) {
        ListNode *p;
        ListNode *a = headA,*b = headB;
        if( !headB || !headA) return NULL;
        while( a != b) {
            a = a ? a->next:headB;
            b = b ? b->next:headA;
        }
        return a;
    }

当然还有一种暴力的求法,先求出len值,然后进行对齐,最后直接比较。

ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) {
        ListNode *cura=headA,*curb= headB;
        int lena = 0,lenb =0;
        while(cura) {
            lena++;
            cura=cura->next;
        }
        while(curb) {
            lenb++;
            curb = curb->next;
        }

        cura = headA;
        curb = headB;

        if(lenb > lena) {
            swap(lena,lenb);
            swap(cura,curb);
        }

        int gap = lena-lenb;

        while(gap--) {
            cura = cura->next;
        }

        while(cura) {
            if(cura == curb) {
                return cura;
            }
            cura = cura->next;
            curb = curb->next;
        }
        return NULL;
    }

两数相加

在这里插入图片描述
这个题目有几个需要注意的点,首先是对节点的初始化。需要一个头结点来满足题意。当然关键的还有一个进位标志符。

ListNode* addTwoNumbers(ListNode* l1, ListNode* l2) {
        ListNode *head=nullptr,*tail=nullptr;
        int carry=0;
        while( l1 || l2 ) {
           int x = l1 ? l1->val:0;
           int y = l2 ? l2->val:0;
           int sum = x+ y+ carry;
           if( ! head) {
               head = tail = new ListNode(sum%10);
            }else {
                tail->next = new ListNode(sum%10);
                tail = tail->next;
            }
            carry = sum/10;
            if(l1) {
                l1 = l1->next;
            }
            if(l2) {
                l2 = l2->next;
            }
        }
        if(carry>0) {
            head = new ListNode(carry);
        }
        return head;
    }

当然了这题也同样可以使用递归,毕竟算一位然后可以向上递归到全位。

ListNode* add(ListNode *l1,ListNode *l2,int a) {
        if(!l1 && !l2) {return a==0 ? nullptr:new ListNode(a);}
        if( l1 != nullptr) {
            a += l1->val;
            l1 = l1->next;
        }
        if( l2 != nullptr) {
            a += l2->val;
            l2 = l2->next;
        }
        return new ListNode(a%10,add(l1,l2,a/10));
    } 

    ListNode* addTwoNumbers(ListNode* l1, ListNode* l2) {
        return add(l1,l2,0);
    }

搜索旋转排序数组

在这里插入图片描述
很明显的二分查找

int search(vector<int>& nums, int target) {
        int i=0,j= nums.size()-1;
        int mid;
        while ( i <= j) {
            mid = (i+j)/2;
            if( nums[mid] == target ) return mid;
            else if( nums[mid] < nums[j]) {
                if(nums[mid] < target &&target <= nums[j]) {
                    i = mid+1;
                }else {
                    j = mid-1;
                }
            }
            else {
                if(nums[i] <= target && target<nums[mid]) {
                    j = mid-1;
                }else {
                    i = mid +1;
                }
            }
        }
        return -1;
    }

逆波兰式

ALDS_3_A
在这里插入图片描述
典型的栈应用,不过在这里需要对输入字符做一些处理,这里使用的Char型,而不是使用string

//
// Created by 17399 on 2022/8/31.
//
#include <iostream>
#include <stack>
#include <cstdlib>
#include <cstdio>
using  namespace  std;

int main() {
    stack<int> a;
    char s[105];
    while(scanf("%s",s)!=EOF){
        if(s[0] == '+') {
            int c = a.top();
            a.pop();
            int d = a.top();
            a.pop();
            a.push(c+d);
        }else if(s[0] == '*') {
            int c = a.top();
            a.pop();
            int d = a.top();
            a.pop();
            a.push(c*d);
        }else if(s[0] == '/') {
            int c = a.top();
            a.pop();
            int d = a.top();
            a.pop();
            a.push(c/d);
        }else if(s[0] == '-') {;;
            int c = a.top();
            a.pop();
            int d = a.top();
            a.pop();
            a.push(d-c);
        } else{

            a.push(atoi(s));

        }
    }
    cout<<a.top()<<endl;
    return 0;
}

队列任务完成

在这里插入图片描述
使用队列来完成,注意需要使用循环队列来保证范围

//
// Created by 17399 on 2022/9/1.
//
#include <iostream>
#include <algorithm>
using  namespace std;

#define maxn  10000005

typedef struct  pp{
  char name[100];
  int t;
}p;

int head,tail;
p q[maxn];
void push(p x) {
    q[tail] =x;
    tail = (tail+1)%maxn;
}

p pop() {
    p x = q[head];
    head = (head + 1)%maxn;
    return x;
}

int main() {
    int n,q1 ;
    cin>>n>>q1;
    int count =0;
    for(int i=1;i<=n;i++) {
        scanf("%s",q[i].name);
        scanf("%d",&q[i].t);
    }
    p u;
    head = 1,tail = n+1;
    while( head != tail ) {
        u = pop();
        int c = u.t;
        int z = min(q1,c);
        u.t -= z;
        count += z;
        if (u.t>0) {
            push(u);
        }else {
            cout<<u.name<<" "<<count<<endl;
        }
    }
    return 0;
}

总结

这些题目质量都是相当不错的,不是很难但不是很容易。首先是C++ 的链表构造部分,这里给出的是两种Leetcode的构造部分,这里就没有采用C语言的malloc构造内存了。

ListNode *p = new ListNode(x);
ListNode *p = new ListNode(x,head->next);

在链表部分,主要是明白遍历与下一个节点的重要性。当然,在leetcode部分,需要对节点的NULL进行判断,否则就会出现相关的错误。
比较好的题目就是两数相加,这是一个关于大数相加的改编题,具有相当的典型意义。

在二叉树部分,需要对递归有深刻的理解。特别是深搜,构造等,都是利用此特性来进行编写。

以上题目需要多看,多加练习!

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值