【算法入门级训练】 - DAY4

链表和二叉树篇



前言

本次算法入门训练主要训练以下二个内容:

  1. 以特殊数据结构为载体的算法结构,比如:数组、链表、栈、二叉树等
  2. 以考察常见算法思想基础的算法题,比如:动态规划、贪心、回溯等
  3. 基于某种场景包装下的1和2

提示:以下是本篇文章正文内容

内容

第一题:给定一个单链表的头结点pHead(该头节点是有值的,比如在下图,它的val是1),长度为n,反转该链表后,返回新链表的表头。

解题思路:有两种常规解法。1.用三个指针来确定节点的位置,防止反转过程中断链的情况,非常简单,只不过要考虑几种特殊情况,链表的题一般来说都要考虑很多情况。2.头插法,设置一个新节点,遍历原链表每次以头插的形式插入到新节点后。
OJ链接:反转链表

public class Solution {
    public ListNode ReverseList(ListNode head) {
        //三个指针法
        //链表为空和链表只有一个节点的情况
        if(head == null || head.next == null){
            return head;
        }
        ListNode prev = head;
        ListNode cur = head.next;
        ListNode last = cur.next;
        while(last != null){
            //反转逻辑
            cur.next = prev;
            prev = cur;
            cur = last;
            last = last.next;
        }
        //链表中只有两个节点和最后两个节点的反转
        cur.next = prev;
        //最后处理现在的尾节点
        head.next = null;
        return cur;
    }
}
public class Solution {
    public ListNode ReverseList(ListNode head) {
        //头插法
        if (head == null || head.next == null) {
            return head;
        }
        ListNode new_head = null;
        while (head != null) { 
            //先从原链表中去掉第一个节点 
            ListNode p = head;
            head = head.next;
            //再将p标识的节点头查到新链表 
            p.next = new_head;
            new_head = p; 
        }
        head = new_head;  
        return head; 
    }
}

第二题:输入两个单调递增的链表,输出两个链表合成后的链表,当然我们需要合成后的链表满足单调不减规则。

解题思路:1.设置一个头节点,用做合并新链表的头节点,两个链表挨个挨个比大小,小的接到新链表当中。2.当然也可以利用递归来一个一个的归并。
OJ链接:链表合并

public class Solution {
    public ListNode Merge(ListNode list1,ListNode list2) {
        //两个链表两个头节点,挨个比大小,小的接到新链表中
        //处理某个链表为空或者都为空的情况
        if(list1 == null){
            return list2;
        }
        if(list2 == null){
            return list1;
        }
        ListNode list = new ListNode(-1);
        ListNode ret = list;
        while(list1 != null && list2 !=null){
            if(list1.val > list2.val){
                //判断接哪个链表
                list.next = list2;
                list = list.next;
                list2 = list2.next;
            }else{
                list.next = list1;
                list = list.next;
                list1 = list1.next;
            }
        }
        //处理有链表没接完的情况
        if(list1 != null){
            list.next = list1;
        }
        if(list2 != null){
            list.next = list2;
        }
        return ret.next;
    }
}
public class Solution {
    public ListNode Merge(ListNode list1,ListNode list2) {
        //递归
        //递归结束条件
        if(list1 == null){
            return list2;
        }
        if(list2 == null){
            return list1;
        }
        //合并中,找到第一个节点
        ListNode ret = null;
        if(list1.val < list2.val){
            ret = list1;
            list1 = list1.next;
        }else{
            ret = list2;
            list2 = list2.next;
        }
        //合并
        ret.next = Merge(list1,list2);
        return ret;
    }
}

第三题:输入两棵二叉树A,B,判断B是不是A的子结构。(ps:我们约定空树不是任意一个树的子结构)

解题思路:递归,因为二叉树都是递归定义的所以递归操作是常见操作。比较过程分为两步,1.先确定起始位置是否符合。2.再从该位置开始判断其左右子树是否符合
OJ链接:树的子结构

public class Solution {
    private boolean CheckChild(TreeNode root1, TreeNode root2) {
        //代表全部都符合
        if (root2 == null ) {
            return true;
        }
        //主树走完了但是子树还没有走完,root2 != null
        if(root1 == null){
            return false;
        }
        //子树节点不相等
        if(root1.val != root2.val){
            return false;
        }
        //深度递归
        return CheckChild(root1.left,root2.left) && CheckChild(root1.right,root2.right);
    }
    public boolean HasSubtree(TreeNode root1, TreeNode root2) {
        //递归
        if (root1 == null || root2 == null) {
            return false;
        }
        boolean flag = false;
        //确定头节点
        if (root1.val == root2.val) {
            //查看左子树,右子树是否符合
            flag = CheckChild(root1,root2);
        }
        //当前头节点不符合,递归左子树右子树
        if(flag != true){
            flag = HasSubtree(root1.left,root2);
        }
        if(flag != true){
            flag = HasSubtree(root1.right,root2);
        }
        return flag;
    }
}

第四题:操作给定的二叉树,将其变换为源二叉树的镜像

解题思路:根据二叉树构建的特性递归就可以完成,只不过要根据题目意思每次递归都要旋转
OJ链接:镜像二叉树

class Solution {
    public TreeNode invertTree(TreeNode root) {
        if(root == null) return null;
        TreeNode tmp = root.left;
        root.left = root.right;
        root.right = tmp;
        invertTree(root.left);
        invertTree(root.right);
        return root;
    }
}

第五题:在一个排序的链表中,存在重复的结点,请删除该链表中重复的结点,重复的结点不保留,返回链表头指针。 例如,链表1->2->3->3->4->4->5 处理后为 1->2->5

解题思路:这题很容易想到用两个指针移动删除,但是要考虑很多情况,1.没有重复节点,2.有部分重复节点,部分重复节点直至最后一个节点。3.全部都是重复节点。要把这几种情况全都考虑进去,代码就很简单了。
OJ链接:删除链表中重复的节点

/*
 public class ListNode {
    int val;
    ListNode next = null;

    ListNode(int val) {
        this.val = val;
    }
}
*/
public class Solution {
    public ListNode deleteDuplication(ListNode pHead) {
        if(pHead == null){
            return pHead;
        }
        //创建一个虚拟头节点是为了处理链表所有节点都是重复节点
        ListNode head = new ListNode(-1);
        head.next = pHead;
        ListNode prev = head;
        ListNode last = prev.next;

        while(last != null){
            //没有重复节点
            while(last.next != null && last.val != last.next.val){
                prev = prev.next;
                last = last.next;
            }
            //有重复节点
            while(last.next != null && last.val == last.next.val){
                last = last.next;
            }
            //说明有重复节点
            if(prev.next != last){
                prev.next = last.next;
            }
            last = last.next;
        }
        return head.next;
    }
}

总结

以上就是今天训练的内容,对于链表的题来说总是有很多特殊的情况需要考虑,但是思路是很简单的,而二叉树的题多是根据递归来围绕展开,对于某些应用场景我们也要学会向着二叉树来转换然后进行思考。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值