剑指offer-------链表、二叉树

剑指offer思路总结

1、链表

1.1 从尾到头打印链表

       题目概述:给出一个链表的表头,从尾到头反过来打印出每个结点的值。

(1)若可以改变链表

      思路:把链表反转,在遍历。

(2)不能改变链表

      思路:可以借助辅助栈的后进先出;

                 可以利用递归实现。

1.2 删除链表的结点

      题目一大致描述:给定一个单向链表的头结点和一个结点指针,要求在O(1)时间内删除该节点

      思路:因为要求是O(1),所以考虑先复制后结点的值,然后删除。需要注意头结点,尾结点情况。

   

      题目二大致描述:链表有序,删除链表中重复的结点

      思路:为了避免头结点可能被删除的情况,引进一个新的头结点,然后使用两个指针pre,cur进行即可。

class Solution {
    public ListNode deleteDuplicates(ListNode head) {
        if(head == null)
            return null;
        ListNode newHead = new ListNode(-1);
        newHead.next = head;
        ListNode cur = head;   //当前节点
        ListNode pre = newHead; //前驱节点
        while(cur != null && cur.next != null){
            if(cur.val == cur.next.val){  //判断是否相同
                int a = cur.val;
                while(cur != null && cur.val == a){  //找出不相同的节点
                    cur = cur.next;
                }
                pre.next = cur;    
                // pre = cur;  如果多了这条,会出错,所以需要注意
            }else{
                pre = cur;  
                cur = cur.next;
            }
        }   
        return newHead.next;
    }
}

      题目三:去重复元素,但每一个只保留一次。

      思路:一次遍历,出现相同就跳一个指针。

class Solution {
    public ListNode deleteDuplicates(ListNode head) {
        ListNode current = head;
        while(current != null && current.next != null){
            if(current.val == current.next.val){
                current.next = current.next.next;
            }else {
                current = current.next;
            }
        }
        return head;
    }
}

1.3 链表中的倒数第k个结点

      题目大致概述:给出一个链表,输出链表中倒数第k个结点。

      思路双指针,一个先走k步,然后同时起步。

1.4 链表中环的入口

     题目概述:一个链表中包含环,找出环的入口

     思路: 双指针,一个每次走一步,一个每次走两步,当相遇时,第二个指针刚好比第一个走多了一个环大小的距离,然后一个指针从头节点重新开始走,相遇时的节点就是环的入口。

1.5 反转链表

    题目概述:给定一个链表,反转该链表。

    思路:双指法。一个指针记录为反转的链表头节点,一个是反转了的链表的头节点。

1.6 合并两个排序的链表

    题目概述:给定俩个递增的链表,合并两个链表,使其依旧是递增排序。

    思路:递归法。

1.7 复杂链表的复制

     题目描述:给定一个复杂的链表,包括一个next指针和一个指向随意地方的指针。复制一份。

     思路:两次遍历法,第一次遍历是在原链表的每一个结点后复制一份新的结点;第二次是对随机指针的复制并分开链表。

    

1.8 二叉搜索树与双向链表

     题目描述:给定一棵二叉搜索树,将二叉搜索树转成一棵排序的双向链表。不能创建新节点,只能调整树中结点指针的指向

     思路:利用递归法。

class Solution {
    public Node pre;    
    public Node head, tail;
    public Node treeToDoublyList(Node root) {
        if(root == null){
            return null;
        }
        process(root);
        tail.right = head;
        head.left = tail;
        return head;
    }
    public void process(Node root){
        if(root == null){
            return;
        }
        process(root.left);
        root.left = pre;
        if(pre == null){
            head = root;
        }else{
            pre.right = root;
        }
        pre = root;
        tail = root;
        process(root.right);
    }
}

1.9  序列化二叉树

       题目概述:实现两个函数,分别用来序列化和反序列化二叉树

       思路:序列化时利用前序遍历,当遇到null ,用&表示;反序列化时,利用一个全局变量,构造即可。

 1.10  两个链表的第一个公共节点

       题目概述:输入两个链表,找出它们的第一个公共节点。

       思路:  采用路径法。也就是让两个指针同时走一遍两个链表,相遇时的点就是第一个公共节点。

public class Solution {
    public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
           ListNode list1 = headA;
           ListNode list2 = headB;
           while(list1 != list2){
               list1 = list1 == null ? headB : list1.next;
               list2 = list2 == null ? headA : list2.next;
           } 
           return list1;
    }
}

2、树

2.1 重建二叉树

      题目概述:输入某个二叉树的前序遍历和中序遍历的结果,重建该二叉树。

      思路:在二叉树的前序遍历序列中,第一个数字总是树的根结点的值。但在中序遍历序列中,根结点的值在序列的中间,左子树的结             点的值位于根结点的值的左边,而右子树的结点的值位于根结点的值的右边。因此我们需要扫描中序遍历序列,才能找到根结点的             值。
  

       
             分别找到了左、右子树的前序遍历序列和中序遍历序列,我们就可以用同样的方法分别去构建左右子树。换句话说,这是一个递归的过程。
 

2.2 二叉树的下一个节点

      题目概述:给定一棵二叉树和其中的一个节点,其中二叉树还包含一个指向父节点的指针,如何找出中序遍历的下一个节点。

      思路:分三种情况进行分析:

                 (1)如果给出的结点有右子树,那么它的下一个节点就是它的右子树的最左子结点;

                 (2)如果给出的结点没有右子树,如果该节点是它父节点的左子树,那么下一个节点就是父节点;

                 (3)如果给出的结点既没有右子树,也不是父节点的左子树,而是父节点的右子树,那么可以沿着指向父节点的指针一直向上遍历,直到找到一个是它父节点的左子结点,若没找到,则下一个节点为null。

2.3 树的子结构

     题目概述:输入两颗二叉树A和B,判断B是不是A的子结构。

     思路:递归法。先判断第一个节点是否相同,若相同,判断以该节点开始的树是否含有子结构;若不是,递归左右子树。

2.4二叉树的镜像。

    题目概述:给定一颗二叉树,输出其镜像二叉树

    思路:利用递归法即可。

2.5 对称二叉树。

    题目概述:给定一颗二叉树,判断这颗二叉树是否为对称二叉树。如果一颗树与他的镜像二叉树相同,则认为他是是一颗对称二叉树

   思路:利用递归遍历法。如果一颗二叉树为对称二叉树,那么他的前序遍历和后续遍历应该是相同的。

2.6 从上到下打印二叉树

    题目一概述:给定一颗二叉树,同一层的节点从左到右打印(不分行)。

    思路:借助一个队列,每次遍历完队列的一个节点时,就将该节点不为空的节点进队列。

    题目二概述:给定一颗二叉树,同一层的节点从左到右打印(分行)。

    思路:和题目一一样,不过需要定义两个变量参数,一个记录当前行的节点数,一个记录下一行的节点数。

    题目三概述:给定一颗二叉树,之字形打印(分行)。

    思路:借助两个栈,一个栈遍历时先进左节点,然后右节点;另一个栈先进右节点,然后进左节点。

2.7 二叉搜索树的后序遍历序列

    题目概述:给定一个数组,判断该数组是否为二叉搜索树的后序遍历结果。

    思路:递归,后序遍历的最后一个元素为根节点。可以根据根节点找出其左子树,右子树,然后分别对其子树进行递归判断即可。

    

2.8 二叉树中和为某一值得路径

    题目概述:给定一个二叉树和一个整数,打印出二叉树中和为节点值得所有路径。(路径就是从根节点到叶子节点形成的路径 )

    思路可以采用递归方法,利用树的前序遍历。


class Solution {
    List<Integer> list = new ArrayList<>();
    List<List<Integer>> lists = new ArrayList<>();
    public List<List<Integer>> pathSum(TreeNode root, int sum) {
        if(root == null){
            return lists;
        }
        pathS(root,sum);
        return lists;
        
    }

    public void pathS(TreeNode root, int sum){
        if(root == null){
            return;
        }
        list.add(root.val);
        sum -= root.val;
        if(root.left == null && root.right == null && sum == 0){  //符合条件,记录下来
            lists.add(new ArrayList(list));
            list.remove(list.size()-1);    //移除list的最后一个元素,回朔上一步
            return;
        }
        pathS(root.left,sum);
        pathS(root.right,sum);
        list.remove(list.size()-1);   
    }
}

2.9 二叉搜索树的第k大节点

      题目概述:给定一个二叉搜索树,找出其中第k大的节点。

      思路:(1) 可以利用二叉树的中序遍历(不过是先从右子节点开始遍历);

                 (2)也可以借助栈进行深度遍历,到k为止。

2.10 二叉树的深度

     题目一概述:二叉树的深度。

     思路:直接采用递归法就可以搞定。

class Solution {
    public int maxDepth(TreeNode root) {
        if(root == null)
            return 0;
        int leftDe = maxDepth(root.left);  //递归计算左子树高度
        int rightDe = maxDepth(root.right); //递归计算右子树高度
        return leftDe > rightDe ? leftDe + 1 : rightDe + 1;    
    }
}

     题目二概述:判断一棵是否为平衡二叉树

     思路递归,但是注意重复节点的遍历。

class Solution {
    public boolean isBalanced(TreeNode root) {
        if(root == null)   
           return true;
        return depth(root) != -1;   
    }

    private int depth(TreeNode root){
        if(root == null)
           return 0;
        int leftDepth = depth(root.left);  //递归左子树
        if(leftDepth == -1){
            return -1;
        }  
        int rightDepth = depth(root.right); //递归右子树
        if(rightDepth == -1){
            return -1;
        } 
        //若子树不满足平衡条件,直接返回 -1 ,否则返回该子树的深度
        return Math.abs(leftDepth - rightDepth) > 1 ? -1 : Math.max(leftDepth , rightDepth) + 1;
    }
}
发布了78 篇原创文章 · 获赞 53 · 访问量 8134

猜你喜欢

转载自blog.csdn.net/qqq3117004957/article/details/105379056