剑指offer第十七题之第二十六题(java详解)

目录

  1. 第17题:合并两个排序的链表
  2. 第18题:树的子结构
  3. 第19题:二叉树的镜像
  4. 第20题:顺时针打印矩阵
  5. 第21题:包含min函数的栈
  6. 第22题:栈的压入、弹出序列
  7. 第23题:从上往下打印二叉树
  8. 第24题:二叉搜索树的后序遍历
  9. 第25题:二叉树中和为某一值的路径
  10. 第26题:复杂链表的复制

    第17题:合并两个排序的链表

/**
 * 题目描述:合并两个排序的链表
 *      输入两个递增排序的链表,合并这两个链表并使新链表中的结点仍然是按照递增排序的。
 *      
 * 解题思路:注意边界检验
 * 
 * @author 焦含寒
 *
 */
public class NO17 {

    public static void main(String[] args) {
        // TODO Auto-generated method stub
        Node node1 = new Node(1);
        Node node2 = new Node(2);
        Node node3 = new Node(3);
        Node node4 = new Node(4);
        Node node5 = new Node(5);
        Node node6 = new Node(6);
        Node node7 = new Node(7);
        Node node8 = new Node(8);
        Node node9 = new Node(9);
        Node node10 = new Node(10);
        Node node11 = new Node(11);
        //第一个链表
        node1.setNext(node3);
        node3.setNext(node5);
        node5.setNext(node7);
        node7.setNext(node9);
        node9.setNext(node11);

        //第二个链表
        node2.setNext(node4);
        node4.setNext(node6);
        node6.setNext(node8);
        node8.setNext(node10);

        print(merge(node1, node2));

    }

    public static Node merge(Node head1,Node head2){
        if(head1 == null)
            return head2;
        else if(head2 == null)
            return head1;

        Node mergeNode = null;
        if(head1.getData() < head2.getData()){
            mergeNode = head1;
            merge(head1.getNext(), head2);
        }else{
            mergeNode = head2;
            merge(head1, head2.getNext());
        }
        return mergeNode;
    }

    private static void print(Node root){
        if(root != null){
            System.out.println(root.getData());
            print(root.getNext());
        }
    }

}

class Node{
    private int data;
    private Node next;

    public Node(int data) {
        super();
        this.data = data;
    }
    public int getData() {
        return data;
    }
    public void setData(int data) {
        this.data = data;
    }
    public Node getNext() {
        return next;
    }
    public void setNext(Node next) {
        this.next = next;
    }

}

## 第18题:树的子结构 ##

import java.util.ArrayList;
import java.util.List;

/**
 * 题目描述:树的子结构
 *      输入两颗二叉树A和B,判断B是不是A的子结构。
 * 
 * @author 焦含寒
 *
 */
public class NO18 {
    public static void main(String[] args){
        //第一棵树
        BinaryTreeNode node1 = new BinaryTreeNode(8);
        BinaryTreeNode node2 = new BinaryTreeNode(8);
        BinaryTreeNode node3 = new BinaryTreeNode(7);
        BinaryTreeNode node4 = new BinaryTreeNode(9);
        BinaryTreeNode node5 = new BinaryTreeNode(2);
        BinaryTreeNode node6 = new BinaryTreeNode(4);
        BinaryTreeNode node7 = new BinaryTreeNode(7);
        node1.setLChild(node2);
        node1.setRChild(node3);
        node2.setLChild(node4);
        node2.setRChild(node5);
        node5.setLChild(node6);
        node5.setRChild(node7);

        //第二棵树
        BinaryTreeNode node_1 = new BinaryTreeNode(8);
        BinaryTreeNode node_2 = new BinaryTreeNode(9);
        BinaryTreeNode node_3 = new BinaryTreeNode(2);
        node_1.setLChild(node_2);
        node_1.setRChild(node_3);
        System.out.println(hasSubTree(node1, node_1));
    }

    public static boolean hasSubTree(BinaryTreeNode root1,BinaryTreeNode root2){
        boolean result = false;
        if(root1 != null && root2 != null){
            if(root1.getData() == root2.getData()){
                result = doseTree1HaveTree2(root1,root2);
                if(!result){
                    result = hasSubTree(root1.getLChild(), root2);
                }
                if(!result){
                    result = hasSubTree(root1.getRChild(), root2);
                }
            }
        }
        return result;
    }

    private static boolean doseTree1HaveTree2(BinaryTreeNode root1, BinaryTreeNode root2) {
        if(root2 == null)
            return true;
        if(root1 == null)
            return false;
        if(root1.getData() != root2.getData())
            return false;


        return doseTree1HaveTree2(root1.getLChild(), root2.getLChild())
                && doseTree1HaveTree2(root1.getRChild(), root2.getRChild());
    }
}

class BinaryTreeNode{
    private int data;
    private BinaryTreeNode LChild;
    private BinaryTreeNode RChild;


    public BinaryTreeNode(int data) {
        super();
        this.data = data;
    }
    public int getData() {
        return data;
    }
    public void setData(int data) {
        this.data = data;
    }
    public BinaryTreeNode getLChild() {
        return LChild;
    }
    public void setLChild(BinaryTreeNode lChild) {
        LChild = lChild;
    }
    public BinaryTreeNode getRChild() {
        return RChild;
    }
    public void setRChild(BinaryTreeNode rChild) {
        RChild = rChild;
    }


}

## 第19题:二叉树的镜像 ##

/**
 * 题目描述:二叉树的镜像
 *      请完成一个函数,输入一个二叉树,该函数输出它的镜像
 * 
 * 解题思路:先先序遍历这棵树的每个结点,如果遍历到的结点有子结点,就交换
 *      它的两个子结点,当交换完所有非叶子结点的左右结点的时候,即结果。
 * @author 焦含寒
 *
 */
public class NO19 {
    public static void main(String[] args){
        BTNode node1 = new BTNode(8);
        BTNode node2 = new BTNode(6);
        BTNode node3 = new BTNode(10);
        BTNode node4 = new BTNode(5);
        BTNode node5 = new BTNode(7);
        BTNode node6 = new BTNode(9);
        BTNode node7 = new BTNode(11);
        node1.setLChild(node2);
        node1.setRChild(node3);
        node2.setLChild(node4);
        node2.setRChild(node5);
        node3.setLChild(node6);
        node3.setRChild(node7);
        mirror(node1);
        print(node1);
    }
    public static void mirror(BTNode root){
        if(root == null)
            return;
        if(root.getLChild() == null && root.getRChild() == null)
            return;

        BTNode temp = root.getLChild();
        root.setLChild(root.getRChild());
        root.setRChild(temp);
        mirror(root.getLChild());
        mirror(root.getRChild());

    }

    private static void print(BTNode root){
        if(root != null){
            System.out.println(root.getData());
            print(root.getLChild());
            print(root.getRChild());
        }
    }
}

class BTNode{
    private int data;
    private BTNode LChild;
    private BTNode RChild;

    public BTNode(int data) {
        super();
        this.data = data;
    }
    public BTNode getLChild() {
        return LChild;
    }
    public void setLChild(BTNode lChild) {
        LChild = lChild;
    }
    public BTNode getRChild() {
        return RChild;
    }
    public void setRChild(BTNode rChild) {
        RChild = rChild;
    }

    public int getData() {
        return data;
    }
    public void setData(int data) {
        this.data = data;
    }


}

## 第20题:顺时针打印矩##

/**
 * 题目描述:顺时针打印矩阵
 *      输入一个矩阵,按照从外向里以顺时针的顺序打印出每一个数字
 *      例如,输入矩阵:
 *          1   2   3   4
 *          5   6   7   8
 *          9   10  11  12
 *          13  14  15  16
 *      打印结果:1、2、3、4、8、12、16、15、14、13、9、5、6、7、11、10
 * 
 * 解题思路:可以矩阵思考成若干的圈,一层嵌套一层。这个问题就变成了两个问题,
 *      一个矩阵应该打印多少圈和每一圈该怎么去打印。先来解决第一个问题,该打
 *      印多少圈?不难发现,每一圈的起点的横纵坐标都是相同的,假设这个矩阵有
 *      rows行,有columns列,当圈数继续进行下去的条件是rows > startX*2 &&
 *      columns > startY*2;再来解决如何打一圈的问题?总共分为四步,第一步
 *      从左到右,第二步从上向下,第三步从右向左,第四步从下到上。注意最后
 *      一圈会退化,可能只会有三步,两步甚至一步。第一步总是需要的,因为打印
 *      一圈至少有一步。需要第二步的前提条件是终止行号大于起始行号,需要第三步
 *      的条件至少有两行两列,也就是除了要求终止行号大于起始行号之外,还需要终
 *      止列号大于起始列号。需要第四步的前提条件是至少三行两列,因此要求终止行
 *      号比起始行号至少大2,同时终止行号大于起始行号。
 *
 * @author 焦含寒
 *
 */
public class NO20 {
    public static void main(String[] args){
        int[][] arr = {{1,2,3,4},
                     {5,6,7,8},
                     {9,10,11,12},
                     {13,14,15,16}};
        printArray(arr);
        printMatixClockwisely(arr);
    }



    private static void printArray(int[][] arr) {
        // TODO Auto-generated method stub
        for(int i =0;i<arr.length;i++){
            for(int j = 0;j<arr[0].length;j++){
                System.out.print(arr[i][j] + " ");
            }
            System.out.println();
        }
    }

    public static void printMatixClockwisely(int[][] arr){
        if(arr == null)
            return;
        int start = 0;
        while(arr[0].length > start*2 && arr.length > start *2){
            printOneCircle(arr,start);
            start++;
        }
    }

    private static void printOneCircle(int[][] arr, int start) {
        // 打印从坐向右的一行
        for(int i = start;i<arr[0].length-start;i++)
            System.out.print(arr[start][i] + " ");

        //判断并打印从上到下的一列
        if(arr.length-1-start > start){
            for(int i = start+1;i<arr.length-1-start;i++)
                System.out.print(arr[i][arr[0].length-1-start] + " ");
        }

        //判断并打印从左向右的一行
        if(arr[0].length-start-1 > start && arr.length-start-1 >start){
            for(int i=arr.length-1-start;i>start;i--){
                System.out.print(arr[arr.length-1-start][i] +  " ");
            }
        }

        //判断并打印从下到上的一列
        if(arr.length-start-1 >start && arr[0].length-start-1 > start  ){
            for(int i=arr.length-1-start;i>start;i--){
                System.out.print(arr[i][start] +  " ");
            }
        }


    }


}

## 第21题:包含min函数的栈 ##

import java.util.Stack;

/**
 * 题目描述:包含min函数的栈
 *      定义栈的数据结构,请在该类型中实现一个能够得到栈的最小元素的min
 *      函数。在该栈中,调用min、push及pop的时间复杂度都是O(1).
 * 
 * 解题思路:最直观的想法就是在栈结构中维护一个最小值,可是这个办法只能调用一次,
 *      当最小值被弹出的时候,就无法确定下一个最小值了。所以更换策略,在栈结构中
 *      再维护一个最小值栈。当数据压到数据栈的时候,要是最小值栈为空或者最顶层元素
 *      大于要压栈元素,就将这个数据压入最小值栈中,要是这个数据大于最小值栈最顶层
 *      元素的话,就将最小值栈的顶层元素的值再压栈一遍。
 * 
 * @author 焦含寒
 *
 */
public class NO21 {
    public static void main(String[] args) {
        MyStack mStack = new MyStack();
        System.out.println(mStack.min());
        mStack.push(3);
        mStack.push(4);
        mStack.push(4);
        mStack.push(10);
        mStack.push(11);
        System.out.println(mStack.min());
    }
}

class MyStack {
    private Stack<Integer> stack1;
    private Stack<Integer> stackHelp;

    public MyStack() {
        stack1 = new Stack<>();
        stackHelp = new Stack<>();
    }

    public void push(int num) {
        stack1.push(num);
        if (stackHelp.size() == 0 || num < stackHelp.peek()) {
            stackHelp.push(num);
        } else {
            stackHelp.push(stackHelp.peek());
        }
    }

    public void pop() {
        stack1.pop();
        stackHelp.pop();
    }

    public Integer min() {
        if (stackHelp.size() == 0)
            return null;
        return stackHelp.peek();
    }
}

## 第22题:栈的压入、弹出序列 ##

import java.util.Stack;

/**
 * 题目描述:栈的压入、弹出序列
 *      输入两个整数序列,第一个序列表示栈的压入顺序,请判断第二个序列是否为该栈的弹出
 *      顺序。假设压入栈的所有数字均不相等。例如序列1、2、3、4、5是某粘的压栈序列,序列
 *      4、5、3、2、1是该压栈序列对应的一个弹出序列,但4、3、5、1、2就不可能是该压栈序列
 *      的弹出序列。
 * 
 * 解题思路:如果下一个弹出的数字刚好是栈顶数字,那么直接弹出。如果下一个弹出的数字不在栈顶
 *      就把压栈序列中还没有入栈的数字压入辅助栈,直到把下一个需要弹出的数字压入栈顶为止。
 *      如果所有的数字都压入栈了仍然没有找到下一个弹出的数字,那么该序列不可能是一个弹出序列。
 * 
 * 
 * @author 焦含寒
 *
 */
public class NO22 {

    public static void main(String[] args) {
        // TODO Auto-generated method stub
        int[] arr1 = {1,2,3,4,5};
        int[] arr2 = {4,3,5,2,1};
        int[] arr3 = {4,3,5,1,2};
        System.out.println(isPopOrder(arr1, arr2));
        System.out.println(isPopOrder(arr1, arr3));
    }

    public static boolean isPopOrder(int[] arr1,int[] arr2){
        if(arr1 == null || arr2 == null)
            return false;

        int point1 = 0;
        Stack<Integer> stack = new Stack();
        for(int i=0;i<arr2.length;i++){
            if(!stack.isEmpty() && stack.peek() == arr2[i]){
                stack.pop();
            }else{
                if(point1 == arr1.length)
                    return false;
                else{
                    do{
                        stack.push(arr1[point1++]);
                    }while(stack.peek()!=arr2[i] && point1 != arr1.length);
                    if(stack.peek() == arr2[i])
                        stack.pop();
                    else
                        return false;
                }
            }
        }
        return true;
    }
}

## 第23题:从上往下打印二叉树 ##

import java.util.LinkedList;
import java.util.Queue;

/**
 * 题目描述:从上往下打印二叉树
 *      从上往下打印出二叉树的每个结点,同一层的结点按照从左到由的顺序打印。
 * 
 * 解题思路:就是实现层次遍历
 * 
 * @author 焦含寒
 *
 */
public class N023 {

    public static void main(String[] args) {
        MBtNode node1 = new MBtNode(8);
        MBtNode node2 = new MBtNode(6);
        MBtNode node3 = new MBtNode(10);
        MBtNode node4 = new MBtNode(5);
        MBtNode node5 = new MBtNode(7);
        MBtNode node6 = new MBtNode(9);
        MBtNode node7 = new MBtNode(11);
        node1.setLChild(node2);
        node1.setRChild(node3);
        node2.setLChild(node4);
        node2.setRChild(node5);
        node3.setLChild(node6);
        node3.setRChild(node7);

        printFromTopToBottom(node1);
    }

    private static void printFromTopToBottom(MBtNode root){
        if(root == null)
            return;
        Queue<MBtNode> queue = new LinkedList<>();
        queue.add(root);
        while(!queue.isEmpty()){
            MBtNode node = queue.poll();
            System.out.println(node.getData());
            if(node.getLChild() != null)
                queue.add(node.getLChild());
            if(node.getRChild() != null)
                queue.add(node.getRChild());
        }
    }

}
class MBtNode{
    private int data;
    private MBtNode LChild;
    private MBtNode RChild;
    public MBtNode(int data) {
        super();
        this.data = data;
    }
    public int getData() {
        return data;
    }
    public void setData(int data) {
        this.data = data;
    }
    public MBtNode getLChild() {
        return LChild;
    }
    public void setLChild(MBtNode lChild) {
        LChild = lChild;
    }
    public MBtNode getRChild() {
        return RChild;
    }
    public void setRChild(MBtNode rChild) {
        RChild = rChild;
    }

}

## 第24题:二叉搜索树的后序遍历 ##

/**
 * 题目描述:二叉搜索树的后序遍历
 *      输入一个整数数组,判断该数组是不是某个二叉搜索树的后续遍历的结果。
 *      如果是则返回true,否则返回false。假设输入的数组的任意两个数字都
 *      互不相同。
 * 
 * 解题思路:后序遍历的序列中,最后一个数字是树的根结点的值。数组中前面的
 *      数字可以分为两部分:第一部分是左子树结点的值,它们都比根结点的值小
 *      ;第二部分是右子树结点的值,它们都比根结点的值大。
 * 
 * @author 焦含寒
 *
 */
public class NO24 {

    public static void main(String[] args) {
        int[] arr = {5,7,6,9,11,10,8};
        System.out.println(verifySequenceOfBST(arr, 0, arr.length-1));

    }

    private static boolean verifySequenceOfBST(int[] arr,int start,int end){
        if(arr == null || arr.length == 0
                || start > end || start < 0 || end <0)
            return false;

        if(start == end)
            return true;

        int root = arr[end];
        int i = start;
        for(;i<=end;i++){
            if(arr[i] > root)
                break;
        }
        int j = i;
        for(;j<=end;j++){
            if(arr[j]<root)
                return false;
        }
        boolean left = true;
        if(i>start){
            left=verifySequenceOfBST(arr, start, i-1);
        }
        boolean right = true;
        if(i<end){
            right = verifySequenceOfBST(arr, i, end-1);
        }

        return left&&right;
    }

}

## 第25题:二叉树中和为某一值的路径 ##

import java.util.Stack;

/**
 * 题目描述:二叉树中和为某一值的路径
 *      输入一颗二叉树和一个整数,打印出二叉树中结点值的和为输入整数的所有路径。
 *      从树的根结点开始往下一直到叶结点所经过的结点形成一条路径。
 * 
 * 解题思路:对二叉树进行前序遍历,访问到某一个结点的时候,就把该结点添加到路径上
 *      并累加该结点的值。如果该结点为叶子结点并且刚好等于输入的整数,则当前路径符
 *      合要求。如果当前结点不是叶子结点,则继续访问它的孩子结点,当结点访问结束之后
 *      递归函数会自动回到它的父节点。因此我们在函数退出之前要在路径上删除当前结点并
 *      减去当前结点的值,以确保返回父结点时路径刚好是从根结点到父节点的路径。其实也
 *      就是一个栈。
 * 
 * @author 焦含寒
 *
 */
public class NO25 {

    public static void main(String[] args) {
        BT_Node root = new BT_Node(10);
        BT_Node node1 = new BT_Node(5);
        BT_Node node2 = new BT_Node(4);
        BT_Node node3 = new BT_Node(7);
        BT_Node node4 = new BT_Node(12);
        root.setLChild(node1);
        root.setRChild(node4);
        node1.setLChild(node2);
        node1.setRChild(node3);
        findPath(root, 22);

    }

    private static void findPath(BT_Node root, int i) {
        if (root == null)
            return;
        Stack<Integer> stack = new Stack<>();
        int currentSum = 0;
        findPath(root, i, stack, currentSum);
    }

    private static void findPath(BT_Node root, int i, Stack<Integer> stack, int currentSum) {
        currentSum += root.getData();
        stack.push(root.getData());
        if (root.getLChild() == null && root.getRChild() == null) {
            if (currentSum == i) {
                System.out.print("找到路径: ");
                for (int path : stack) {
                    System.out.print(path + " ");
                }
                System.out.println();
            }
        }
        if (root.getLChild() != null) {
            findPath(root.getLChild(), i, stack, currentSum);
        }
        if (root.getRChild() != null) {
            findPath(root.getRChild(), i, stack, currentSum);
        }
        stack.pop();

    }

}

class BT_Node {
    private int data;
    private BT_Node LChild;
    private BT_Node RChild;

    public BT_Node(int data) {
        super();
        this.data = data;
    }

    public int getData() {
        return data;
    }

    public void setData(int data) {
        this.data = data;
    }

    public BT_Node getLChild() {
        return LChild;
    }

    public void setLChild(BT_Node lChild) {
        LChild = lChild;
    }

    public BT_Node getRChild() {
        return RChild;
    }

    public void setRChild(BT_Node rChild) {
        RChild = rChild;
    }

}

## 第26题:复杂链表的复制 ##

/**
 * 题目描述:复杂链表的复制
 *      请实现函数,复制一个复杂链表,在复杂链表中,每个结点除了有一个m_pNext指针
 *      指向下一个结点外,还有一个m_pSibling指向链表中的任意结点或者NULL
 * 
 * 解题思路:先建立链表再去一个一个挂sibling结点,时间复杂度O(n^2);哈希表又牺牲了
 *      空间,下面是一个巧妙的办法
 * 
 * @author 焦含寒
 *
 */
public class N026 {

    public static void main(String[] args) {
        // TODO Auto-generated method stub
        ComplexListNode node1 = new ComplexListNode(1);
        ComplexListNode node2 = new ComplexListNode(2);
        ComplexListNode node3 = new ComplexListNode(3);
        ComplexListNode node4 = new ComplexListNode(4);
        ComplexListNode node5 = new ComplexListNode(5);
        node1.next = node2;
        node2.next = node3;
        node3.next = node4;
        node4.next = node5;
        node1.sibling = node3;
        node2.sibling = node5;
        node4.sibling = node2;
        ComplexListNode result = clone(node1);
        while (result != null) {
            System.out.println(result.data);
            result = result.next;
        }
    }

    private static ComplexListNode clone(ComplexListNode head) {
        colneNodes(head);
        copySiblingNodes(head);
        return sparateNodes(head);
    }

    private static ComplexListNode sparateNodes(ComplexListNode head) {
        ComplexListNode node = head;
        ComplexListNode cloneHead = null;
        ComplexListNode cloneNode = null;
        if (node != null) {
            cloneNode = node.next;
            cloneHead = cloneNode;
            node.next = cloneNode.next;
            node = node.next;
        }
        while (node != null) {
            cloneNode.next = node.next;
            cloneNode = cloneNode.next;
            node.next = cloneNode.next;
            node = node.next;
        }
        return cloneHead;
    }

    private static void copySiblingNodes(ComplexListNode head) {
        // TODO Auto-generated method stub
        ComplexListNode node = head;
        while (node != null) {
            ComplexListNode cloneNode = node.next;

            if (node.sibling != null)
                cloneNode.sibling = node.sibling.next;
            node = cloneNode.next;
        }

    }

    private static void colneNodes(ComplexListNode head) {
        // TODO Auto-generated method stub
        ComplexListNode node = head;
        while (node != null) {
            ComplexListNode cloneNode = new ComplexListNode(node.data);
            cloneNode.next = node.next;
            node.next = cloneNode;
            node = cloneNode.next;
        }

    }

}

class ComplexListNode {
    int data;
    ComplexListNode next;
    ComplexListNode sibling;

    public ComplexListNode(int data) {
        super();
        this.data = data;
    }
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值