sword35

  1. 调整数组顺序使奇数位于偶数前面
题目要求:  
实现一个函数来调整数组中的数字,使得所有奇数位于数组的前半部分,偶数位于后半部分。  

解题思路:  
其实我想到的第一个思路就是用双指针从两端向中间扫描,处理过程与快排很相似,时间复杂度o(n)  
,这应该是最快的解法了。思路简单,就当复习下快排吧。至于复杂度更高的解法,我就不再写了。  
书中提到了一点,是将判断部分写成函数,这样能够提升代码的可扩展性,这的确是很好的一个提醒。  
那样处理之后,这一类问题(非负数在前,负数在后;能被3整除的在前,不能的在后;)都只需修改  
下判断函数即可解决。  
package chapter3;
/**
 * Created by ryder on 2017/7/14.
 * 调整数组顺序使奇数位于偶数前面
 */
public class P129_ReorderArray {
    public static void reorder(int[] data){
        if(data==null||data.length<2)
            return;
        int left=0,right=data.length-1;
        while(left<right){
            while (left<right&&!isEven(data[left]))
                left++;
            while (left<right&&isEven(data[right]))
                right--;
            //分别找到奇数和偶数进行交换
            if(left<right){
                int temp=data[left];
                data[left]=data[right];
                data[right]=temp;
            }
        }
    }
    public static boolean isEven(int n){
        return (n&1)==0;
    }
    public static void main(String[] args){
        int[] data = {1,2,3,4,5,7,7};
        reorder(data);
        for(int i=0;i<data.length;i++) {
            System.out.print(data[i]);
            System.out.print('\t');
        }
        System.out.println();
    }
}
  1. 链表中倒数第k个节点
题目要求:  
求链表中倒数第k个节点。链表的尾节点定义为倒数第1个节点。  

解题思路:  
如果先求链表的长度,计算后再从头遍历,虽然时间复杂度是o(n),但需要两遍  
扫描。更好的方式是使用两个距离为k的指针向右移动,这种方式只需扫描一遍。  
chapter3主要考察细节,这道题也不例外。需要注意链表是否为空,链表长度  
是否达到k,k是否是个正数。  
package structure;
/**
 * Created by ryder on 2017/6/13.
 */
public class ListNode<T> {
    public T val;
    public ListNode<T> next;
    public ListNode(T val){
        this.val = val;
        this.next = null;
    }
    @Override
    public String toString() {
        StringBuilder ret = new StringBuilder();
        ret.append("[");
        for(ListNode cur = this;;cur=cur.next){
            if(cur==null){
                ret.deleteCharAt(ret.lastIndexOf(" "));
                ret.deleteCharAt(ret.lastIndexOf(","));
                break;
            }
            ret.append(cur.val);
            ret.append(", ");
        }
        ret.append("]");
        return ret.toString();
    }
}
package chapter3;
import structure.ListNode;
/**
 * Created by ryder on 2017/7/14.
 * 链表中倒数第k个节点
 */
public class P134_KthNodeFromEnd {
    public static ListNode<Integer> findKthToTail(ListNode<Integer> head,int k){
        if(head==null||k<=0)
            return null;
        ListNode<Integer> slow=head,fast=head;
        for(int i=0;i<k;i++){
            //i==k-1,第三个测试例通不过
            if(fast.next!=null || i==k-1)
                fast = fast.next;
            else
                return null;
        }
        while(fast!=null){
            slow = slow.next;
            fast = fast.next;
        }
        return slow;
    }
    public static void main(String[] args){
        ListNode<Integer> head = new ListNode<>(1);
        head.next= new ListNode<>(2);
        head.next.next = new ListNode<>(3);
        System.out.println(findKthToTail(head,1).val);
        System.out.println(findKthToTail(head,2).val);
        System.out.println(findKthToTail(head,3).val);
        System.out.println(findKthToTail(head,4));
    }
}
  1. 链表中环的入口节点
题目要求:  
假设一个链表中包含环,请找出入口节点。若没有环则返回null。  

解题思路:  
当然可以使用遍历。顺序访问,当第二次访问同一个节点时,  
那么那个节点就是入口节点。不过这种解法需要o(n)的空间。  
能不能把空间降为o(1),时间依旧为o(n)。当然可以。这种  
解法的思路是:首先申请两个指针,快指针一次前进两步,  
慢指针一次前进一步,初始化都再链表头部。然后开始移动,  
如果他们指向了同一个节点,则证明有环,否则没环。当他  
们指向了同一个节点时,慢指针再次初始化,指向头结点。  
快慢指针前进步数都改为1,当他们再次指向同一个节点,  
这个节点就是环的入口节点。不明白的话请先看证明过程链  
接,然后再看我说的思路总结。  
#include <stdio.h>

struct ListNode {
    int val;
    ListNode *next;
    ListNode(int x) : val(x), next(NULL) {}
};

class Solution {
public:
    ListNode *detectCycle(ListNode *head) {
        ListNode *fast = head;
        ListNode *slow = head;
        ListNode *meet = NULL;
        while(fast){
            slow = slow->next;           //先各走一步
            fast = fast->next;
            if (!fast){                  //fast到链表尾,就说明没有环,结点为奇数个
                return NULL;
            }
            fast = fast->next;           //fast再走一步
            if (fast == slow){           //记录相遇位置
                meet = fast;
                break;
            }
        }

        while(head && meet){          //head和meet相遇就是环起点
            if (head == meet){
                return head;
            }
            head = head->next;
            meet = meet->next;
        }
        return NULL;
    }
};

int main(){
    ListNode a(1);
    ListNode b(2);
    ListNode c(3);
    ListNode d(4);
    ListNode e(5);
    ListNode f(6);
    ListNode g(7);
    a.next = &b;
    b.next = &c;
    c.next = &d;
    d.next = &e;
    e.next = &f;
    f.next = &g;
    g.next = &c;
    Solution solve;
    ListNode *node = solve.detectCycle(&a);
    if (node){
        printf("%d\n", node->val);
    }
    else{
        printf("NULL\n");
    }
    return 0;
}

  1. 反转链表
解题思路:  
想要链表反转时不断裂,至少需要3个变量记录,pre,cur,post。与前面的题目类似,  
初始化pre为null,cur为head,post为head.next。初始化之前要注意检查链表的长度。  
package structure;
/**
 * Created by ryder on 2017/6/13.
 */
public class ListNode<T> {
    public T val;
    public ListNode<T> next;
    public ListNode(T val){
        this.val = val;
        this.next = null;
    }
    @Override
    public String toString() {
        StringBuilder ret = new StringBuilder();
        ret.append("[");
        for(ListNode cur = this;;cur=cur.next){
            if(cur==null){
                ret.deleteCharAt(ret.lastIndexOf(" "));
                ret.deleteCharAt(ret.lastIndexOf(","));
                break;
            }
            ret.append(cur.val);
            ret.append(", ");
        }
        ret.append("]");
        return ret.toString();
    }
}
package chapter3;
import structure.ListNode;
/**
 * Created by ryder on 2017/7/14.
 * 反转链表
 */
public class P142_ReverseList {
    public static ListNode<Integer> reverseList(ListNode<Integer> head){
        if(head==null || head.next==null)
            return head;
        ListNode<Integer> pre = null;
        ListNode<Integer> cur = head;
        ListNode<Integer> post = head.next;
        while(true){
            cur.next = pre;
            pre = cur;
            cur = post;
            if(post!=null)
                post = post.next;
            else
                return pre;
        }
    }

    //写法更简单
    public static ListNode<Integer> reverseList(ListNode<Integer> head) {
        if (head == null || head.next == null)
            return head;
        ListNode<Integer> newHead = null;
        while (head != null) {
            ListNode<Integer> next = head.next;
            head.next = newHead;
            newHead = head;
            head = next;
        }
        return newHead;

    }
    public static void main(String[] args){
        ListNode<Integer> head = new ListNode<>(1);
        head.next= new ListNode<>(2);
        head.next.next = new ListNode<>(3);
        System.out.println(head);
        head = reverseList(head);
        System.out.println(head);
    }
}
  1. 合并两个排序的链表
题目要求:  
输入两个递增排序的链表,要求合并后保持递增。  

解题思路:  
这个题目是二路链表归并排序的一部分,或者说是最关键的归并函数部分。熟悉归并排序  
的话做这个题应该很容易。思路很简单,注意链表的next指针,初始条件,结束条件即可。  
package structure;
/**
 * Created by ryder on 2017/6/13.
 */
public class ListNode<T> {
    public T val;
    public ListNode<T> next;
    public ListNode(T val){
        this.val = val;
        this.next = null;
    }
    @Override
    public String toString() {
        StringBuilder ret = new StringBuilder();
        ret.append("[");
        for(ListNode cur = this;;cur=cur.next){
            if(cur==null){
                ret.deleteCharAt(ret.lastIndexOf(" "));
                ret.deleteCharAt(ret.lastIndexOf(","));
                break;
            }
            ret.append(cur.val);
            ret.append(", ");
        }
        ret.append("]");
        return ret.toString();
    }
}
package chapter3;
import structure.ListNode;
/**
 * Created by ryder on 2017/7/14.
 * 合并两个排序的链表
 */
public class P145_MergeSortedLists {
    public static ListNode<Integer> merge(ListNode<Integer> head1,ListNode<Integer> head2){
        if(head1==null)
            return head2;
        if(head2==null)
            return head1;
       ListNode<Integer> index1 = head1;
       ListNode<Integer> index2 = head2;
       ListNode<Integer> index = null;
       if(index1.val<index2.val) {
           index = index1;
           index1 = index1.next;
       }
       else {
           index = index2;
           index2 = index2.next;
       }
       while(index1!=null && index2!=null){
           if(index1.val<index2.val) {
               index.next = index1;
               index = index.next;
               index1 = index1.next;
           }
           else {
               index.next = index2;
               index = index.next;
               index2 = index2.next;
           }
       }
       if(index1!=null)
           index.next = index1;
       else
           index.next = index2;
       return head1.val<head2.val?head1:head2;
    }
    public static void main(String[] args){
        ListNode<Integer> head1 = new ListNode<>(1);
        head1.next= new ListNode<>(3);
        head1.next.next = new ListNode<>(5);
        head1.next.next.next = new ListNode<>(7);
        ListNode<Integer> head2 = new ListNode<>(2);
        head2.next= new ListNode<>(4);
        head2.next.next = new ListNode<>(6);
        head2.next.next.next = new ListNode<>(8);
        System.out.println(head1);
        System.out.println(head2);
        ListNode<Integer> head =merge(head1,head2);
        System.out.println(head);
    }
}
  1. 树的子结构
题目要求:  
输入两棵二叉树A和B,判断B是不是A的子结构。  

解题思路:  
当A有一个节点与B的根节点值相同时,则需要从A的那个节点开始严格匹配,对应于下面的  
tree1HasTree2FromRoot函数。如果匹配不成功,则返回到开始匹配的那个节点,对它的  
左右子树继续判断是否与B的根节点值相同,重复上述过程。  
package structure;
/**
 * Created by ryder on 2017/6/12.
 * 树节点
 */
public class TreeNode<T> {
    public T val;
    public TreeNode<T> left;
    public TreeNode<T> right;
    public TreeNode(T val){
        this.val = val;
        this.left = null;
        this.right = null;
    }
}
package chapter3;
import structure.TreeNode;
/**
 * Created by ryder on 2017/7/14.
 * 树的子结构
 */
public class P148_SubstructureInTree {
    public static boolean hasSubtree(TreeNode<Integer> root1, TreeNode<Integer> root2){
        if(root2==null)
            return true;
        if(root1==null)
            return false;
        if(root1.val.equals(root2.val)){
            if(tree1HasTree2FromRoot(root1,root2))
                return true;
        }
        return hasSubtree(root1.left,root2) || hasSubtree(root1.right,root2);
    }
    public static boolean tree1HasTree2FromRoot(TreeNode<Integer> root1, TreeNode<Integer> root2){
        if(root2==null)
            return true;
        if(root1==null)
            return false;
        if(root1.val.equals(root2.val) && tree1HasTree2FromRoot(root1.left,root2.left) && tree1HasTree2FromRoot(root1.right,root2.right))
            return true;
        else
            return false;

    }
    public static void main(String[] args){
        TreeNode<Integer> root1 = new TreeNode<>(8);
        root1.left = new TreeNode<>(8);
        root1.right = new TreeNode<>(7);
        root1.left.left = new TreeNode<>(9);
        root1.left.right = new TreeNode<>(2);
        root1.left.right.left = new TreeNode<>(4);
        root1.left.right.right = new TreeNode<>(7);
        TreeNode<Integer> root2 = new TreeNode<>(8);
        root2.left = new TreeNode<>(9);
        root2.right = new TreeNode<>(2);
        TreeNode<Integer> root3 = new TreeNode<>(2);
        root3.left = new TreeNode<>(4);
        root3.right = new TreeNode<>(3);
        System.out.println(hasSubtree(root1,root2));
        System.out.println(hasSubtree(root1,root3));
    }
}
  1. 二叉树的镜像
题目要求:  
求一棵二叉树的镜像。  

解题思路:  
二叉树的镜像,即左右子树调换。从上到下,递归完成即可。  
package structure;
import java.util.LinkedList;
import java.util.Queue;
/**
 * Created by ryder on 2017/6/12.
 * 树节点
 */
public class TreeNode<T> {
    public T val;
    public TreeNode<T> left;
    public TreeNode<T> right;
    public TreeNode(T val){
        this.val = val;
        this.left = null;
        this.right = null;
    }
    //层序遍历
    @Override
    public String toString() {
        StringBuilder stringBuilder = new StringBuilder("[");
        Queue<TreeNode<T>> queue = new LinkedList<>();
        queue.offer(this);
        TreeNode<T> temp;
        while(!queue.isEmpty()){
            temp = queue.poll();
            stringBuilder.append(temp.val);
            stringBuilder.append(",");
            if(temp.left!=null)
                queue.offer(temp.left);
            if(temp.right!=null)
                queue.offer(temp.right);
        }
        stringBuilder.deleteCharAt(stringBuilder.lastIndexOf(","));
        stringBuilder.append("]");
        return stringBuilder.toString();
    }
}
package chapter4;
import structure.TreeNode;
/**
 * Created by ryder on 2017/7/15.
 * 二叉树的镜像
 */
public class P151_MirrorOfBinaryTree {
    public static void mirrorRecursively(TreeNode<Integer> root){
        if(root==null)
            return;
        if(root.left==null&&root.right==null)
            return;
        TreeNode<Integer> temp = root.left;
        root.left = root.right;
        root.right = temp;
        mirrorRecursively(root.left);
        mirrorRecursively(root.right);
    }
    public static void main(String[] args){
        TreeNode<Integer> root = new TreeNode<>(8);
        root.left = new TreeNode<>(6);
        root.right = new TreeNode<>(10);
        root.left.left = new TreeNode<>(5);
        root.left.right = new TreeNode<>(7);
        root.right.left = new TreeNode<>(9);
        root.right.right = new TreeNode<>(11);
        System.out.println(root);
        mirrorRecursively(root);
        System.out.println(root);
    }
}
  1. 对称的二叉树
题目要求:  
判断一棵二叉树是不是对称的。如果某二叉树与它的镜像一样,称它是对称的。  

解题思路:  
比较直接的思路是比较原树与它的镜像是否一样。书中就是用的这种方式  
(比较二叉树的前序遍历和对称前序遍历)。但这种思路下,树的每个节  
点都要读两次,也就是遍历两遍。  
其实可以只遍历一次完成判断:我们可以通过判断待判断二叉树的左子树  
与右子树是不是对称的来得知该二叉树是否是对称的。   
package structure;
import java.util.LinkedList;
import java.util.Queue;
/**
 * Created by ryder on 2017/6/12.
 * 树节点
 */
public class TreeNode<T> {
    public T val;
    public TreeNode<T> left;
    public TreeNode<T> right;
    public TreeNode(T val){
        this.val = val;
        this.left = null;
        this.right = null;
    }
    //层序遍历
    @Override
    public String toString() {
        StringBuilder stringBuilder = new StringBuilder("[");
        Queue<TreeNode<T>> queue = new LinkedList<>();
        queue.offer(this);
        TreeNode<T> temp;
        while(!queue.isEmpty()){
            temp = queue.poll();
            stringBuilder.append(temp.val);
            stringBuilder.append(",");
            if(temp.left!=null)
                queue.offer(temp.left);
            if(temp.right!=null)
                queue.offer(temp.right);
        }
        stringBuilder.deleteCharAt(stringBuilder.lastIndexOf(","));
        stringBuilder.append("]");
        return stringBuilder.toString();
    }
}
package chapter4;
import structure.TreeNode;
import java.util.LinkedList;
import java.util.Queue;
/**
 * Created by ryder on 2017/7/15.
 * 对称的二叉树
 */
public class P159_SymmetricalBinaryTree {
    //递归实现
    public static boolean isSymmetrical(TreeNode<Integer> root){
        if(root==null)
            return true;
        if(root.left==null && root.right==null)
            return true;
        if(root.left==null || root.right==null)
            return false;
        return isSymmetrical(root.left,root.right);
    }
    public static boolean isSymmetrical(TreeNode<Integer> root1,TreeNode<Integer> root2){
        if(root1==null && root2==null)
            return true;
        if(root1==null || root2==null)
            return false;
        if(!root1.val.equals(root2.val))
            return false;
        return isSymmetrical(root1.left,root2.right) && isSymmetrical(root1.right,root2.left);
    }
    //迭代实现
    public static boolean isSymmetrical2(TreeNode<Integer> root){
        if(root==null)
            return true;
        if(root.left==null && root.right==null)
            return true;
        if(root.left==null || root.right==null)
            return false;
        Queue<TreeNode<Integer>> queueLeft = new LinkedList<>();
        Queue<TreeNode<Integer>> queueRight = new LinkedList<>();
        queueLeft.offer(root.left);
        queueRight.offer(root.right);
        TreeNode<Integer> tempLeft,tempRight;
        while(!queueLeft.isEmpty()|| !queueRight.isEmpty()){
            tempLeft = queueLeft.poll();
            tempRight = queueRight.poll();
            if(tempLeft.val.equals(tempRight.val)){
                if(tempLeft.left!=null)
                    queueLeft.offer(tempLeft.left);
                if(tempLeft.right!=null)
                    queueLeft.offer(tempLeft.right);
                if(tempRight.right!=null)
                    queueRight.offer(tempRight.right);
                if(tempRight.left!=null)
                    queueRight.offer(tempRight.left);
            }
            else
                return false;
        }
        if(queueLeft.isEmpty() && queueRight.isEmpty())
            return true;
        else
            return false;
    }
    public static void main(String[] args){
        TreeNode<Integer> root = new TreeNode<>(8);
        root.left = new TreeNode<>(6);
        root.right = new TreeNode<>(6);
        root.left.left = new TreeNode<>(5);
        root.left.right = new TreeNode<>(7);
        root.right.left = new TreeNode<>(7);
        root.right.right = new TreeNode<>(5);
        System.out.println(isSymmetrical(root));
        System.out.println(isSymmetrical2(root));
    }
}
  1. 顺时针打印矩阵
题目要求:  
输入一个矩阵,按照从外向里以顺时针的顺序一次打印出每一个数字。  
如果输入如下矩阵:  

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。

解题思路:  
此题的任务清晰明了,需要小心的是要考虑清楚边界情况。  
上例中,环绕一次后,剩下的矩阵行数为2,列数为2,可以看成一个新的矩阵,  
继续环绕打印。可见,此题可以用递归解决。  
示例中行数与列数是相等的,所以能够组成完整的环(完整指能够环绕一圈);  
其实,只要行数和列数中,比较小的那个是偶数,就能够组成完整的环。  
如果行数和列数中比较小的那个是奇数,则递归到终止前的剩余元素无法成环。  
如果较小的是行数,则剩余元素的组成的形状类似于“|”;如果较小的是列数,  
则剩余元素的组成的形状类似于“—”。  

重点:  
因此,当未访问行数和未访问列数都大于等于2时,按照完整环的逻辑递归访问  
即可。当不满足上述条件,判断剩余元素是“|”型还是“—”型,然后按照不完整环  
的逻辑访问即可。  
package chapter4;

/**
 * Created by ryder on 2017/7/16.
 *
 */
public class P161_PrintMatrix {
    public static void printMatrix(int[][] data){
        if(data==null)
            return ;
        if(data.length==0||data[0].length==0)
            return ;
        int rowMax = data.length-1,rowLen = data.length;
        int colMax =data[0].length-1,colLen = data[0].length;
        int row=0,col=0,round=0;
        while(rowLen-2*row>1 && colLen-2*col>1){
            for(;col<=colMax-round;col++){
                System.out.print(data[row][col]);
                System.out.print("\t");
            }
            for(col=col-1,row=row+1;row<rowMax-round;row++){
                System.out.print(data[row][col]);
                System.out.print("\t");
            }
            for(;col>=round;col--){
                System.out.print(data[row][col]);
                System.out.print("\t");
            }
            for(col=col+1,row=row-1;row>round;row--){
                System.out.print(data[row][col]);
                System.out.print("\t");
            }
            row=row+1;
            col=col+1;
            round++;
        }
        //如果行数与列数中较小的那个是偶数,则能组成完整的环,在while中就完成了遍历
        if(rowLen-2*row==0 || colLen-2*col==0){
            System.out.println();
        }
        //如果行数与列数中较小的是行数,且是奇数,则还需补充访问一行
        if(rowLen-2*row==1){
            for(;col<=colMax-round;col++){
                System.out.print(data[row][col]);
                System.out.print("\t");
            }
            System.out.println();
        }
        //如果行数与列数中较小的是列数,且是奇数,则还需补充访问一列
        if(colLen-2*col==1){
            for(;row<=rowMax-round;row++){
                System.out.print(data[row][col]);
                System.out.print("\t");
            }
            System.out.println();
        }

    }
    public static void main(String[] args){
        int[][] data1={
                {1,2,3,4},
                {12,13,14,5},
                {11,16,15,6},
                {10,9,8,7},
        };
        int[][] data2={
                {1,2,3,4},
                {10,11,12,5},
                {9,8,7,6},
        };
        int[][] data3={
                {1,2,3},
                {10,11,4},
                {9,12,5},
                {8,7,6},
        };
        int[][] data4={
                {1,2,3},
                {8,9,4},
                {7,6,5},
        };
        printMatrix(data1);
        printMatrix(data2);
        printMatrix(data3);
        printMatrix(data4);
    }
}
  1. 包含min函数的栈
题目要求:  
定义栈的数据结构,请在该类型中实现一个能够得到栈的最小元素的min函数。  
要求在该栈中,调用min,push及pop的时间复杂度都是o(1)。  

解题思路:  
期望获知当前栈的最小值,最直接的方法是全部弹出求最小值,然后再全部压入。  
这种方式时间消耗较大。另一种思路,可以用空间换时间:自己实现一个栈,栈中  
有记录数据的数据栈,同时也有记录最小值的min栈,本帖就是采用的此方法。记  
得曾经看过一个更巧妙地方法,用一个变量来记录最小值,压入与弹出都会因一定  
的规则修改该变量,思路比较精奇,可遇不可求,有兴趣的可以去搜搜看,比如:  
http://www.cnblogs.com/javawebsoa/archive/2013/05/21/3091727.html  
package chapter4;
import java.util.Stack;
/**
 * Created by ryder on 2017/7/16.
 * 包含min函数的栈
 */
public class P165_StackWithMin {
    public static void main(String[] args){
        StackWithMin<Integer> stack = new StackWithMin<>();
        stack.push(3);
        stack.push(4);
        stack.push(2);
        stack.push(1);
        System.out.println(stack.min());
        stack.pop();
        System.out.println(stack.min());
        stack.pop();
        System.out.println(stack.min());
        stack.pop();
        System.out.println(stack.min());
        stack.pop();
        System.out.println(stack.min());
    }
}
class StackWithMin<T extends Comparable>{
    private Stack<T> stackData = new Stack<>();
    private Stack<T> stackMin = new Stack<>();
    public void push(T data){
        stackData.push(data);
        if(stackMin.isEmpty())
            stackMin.push(data);
        else{
            T temp = stackMin.peek();
            if(temp.compareTo(data)<0)
                stackMin.push(temp);
            else
                stackMin.push(data);
        }
    }
    public T pop(){
        if(stackData.isEmpty())
            return null;
        stackMin.pop();
        return stackData.pop();
    }
    public T min(){
        if(stackMin.isEmpty())
            return null;
        return stackMin.peek();
    }
}
  1. 栈的压入弹出序列
题目要求:  
输入两个整数序列,第一个序列表示栈的压入顺序,判断第二个序列是否为该栈的弹出序序列。  
假设压入栈的所有数字均不相等。例如,压入序列为(1,2,3,4,5),序列(4,5,3,2,1)是它的  
弹出序列,而(4,3,5,1,2)不是。  

解题思路:  
对于一个给定的压入序列,由于弹出的时机不同,会出现多种弹出序列。如果是选择题,依照后  
进先出的原则,复现一下栈的压入弹出过程就很容易判断了。写成程序同样如此,主要步骤如下:  

步骤1:栈压入序列第一个元素,弹出序列指针指弹出序列的第一个;  
步骤2:判断栈顶元素是否等于弹出序列的第一个元素:  
步骤2.1:如果不是,压入另一个元素,进行结束判断,未结束则继续执行步骤2;  
步骤2.2:如果是,栈弹出一个元素,弹出序列指针向后移动一位,进行结束判断,  
未结束则继续执行步骤2;  

结束条件:如果弹出序列指针还没到结尾但已经无元素可压入,则被测序列不是弹出序列。  
         如果弹出序列指针以判断完最后一个元素,则被测序列是弹出序列。  
当然,进行判断前需要进行一些检查,比如传入参数是否为null,压入序列与弹出序列长度是否一致。  
package chapter4;
import java.util.Stack;
/**
 * Created by ryder on 2017/7/17.
 * 栈的压入弹出序列
 */
public class P168_StackPushPopOrder {
    public static boolean isPopOrder(int[] pushSeq,int[] popSeq){
        if(pushSeq==null||popSeq==null||pushSeq.length!=popSeq.length)
            return false;
        Stack<Integer> stack = new Stack<>();
        int pushSeqIndex=0,popSeqIndex=0;
        //出栈序列全部出栈跳出,或者返回false跳出
        while (popSeqIndex<popSeq.length){
            //当为空或者栈顶不是出栈序列指向的当前元素就入栈
            if(stack.isEmpty()||stack.peek()!=popSeq[popSeqIndex]) {
                if(pushSeqIndex<pushSeq.length)
                    stack.push(pushSeq[pushSeqIndex++]);
                //指针指向push入栈序列没有数据的位置了
                else
                    return false;
            }
            else{
                stack.pop();
                popSeqIndex++;
            }
        }
        return true;
    }
    public static void main(String[] args){
        int[] push = {1,2,3,4,5};
        int[] pop1 = {4,5,3,2,1};
        int[] pop2 = {4,3,5,1,2};
        System.out.println(isPopOrder(push,pop1));
        System.out.println(isPopOrder(push,pop2));
    }
}
  1. 32-1 从上到下打印二叉树

    //层序遍历
    public static List<Integer> levelorder(TreeNode<Integer> node) {
        Queue<TreeNode<Integer>> queue = new LinkedList<>();
        List<Integer> list = new LinkedList<>();
        TreeNode<Integer> temp = null;
        if (node == null) return list;
        queue.add(node);
        while (!queue.isEmpty()) {
            temp = queue.poll();
            list.add(temp.val);
            if (temp.left != null) queue.offer(temp.left);
            if (temp.right != null) queue.offer(temp.right);
        }
        return list;
    }
  1. 32-2 分行从上到下打印二叉树
题目要求:  
从上到下按层打印二叉树,同一层的节点按从左到右的顺序打印 ,每一层打印一行。  

解题思路:  
同样是层序遍历,与上一题不同的是,此处要记录每层的节点个数,在每层打印结束后多打印一个回车符。  
package structure;
import java.util.LinkedList;
import java.util.Queue;
/**
 * Created by ryder on 2017/6/12.
 * 树节点
 */
public class TreeNode<T> {
    public T val;
    public TreeNode<T> left;
    public TreeNode<T> right;
    public TreeNode(T val){
        this.val = val;
        this.left = null;
        this.right = null;
    }
}
package chapter4;
import structure.TreeNode;
import java.util.LinkedList;
import java.util.Queue;
/**
 * Created by ryder on 2017/7/18.
 * 分行从上到下打印二叉树
 */
public class P174_printTreeInLine {
    public static void printTreeInLine(TreeNode<Integer> root){
        if(root==null)
            return;
        Queue<TreeNode<Integer>> queue = new LinkedList<>();
        queue.offer(root);
        TreeNode<Integer> temp;
        while (!queue.isEmpty()){
            for(int size=queue.size();size>0;size--){
                temp = queue.poll();
                System.out.print(temp.val);
                System.out.print("\t");
                if(temp.left!=null)
                    queue.offer(temp.left);
                if(temp.right!=null)
                    queue.offer(temp.right);
            }
            System.out.println();
        }
    }
    public static void main(String[] args){
        //            1
        //          /   \
        //         2     3
        //       /  \   / \
        //      4    5 6   7
        TreeNode<Integer> root = new TreeNode<Integer>(1);
        root.left = new TreeNode<Integer>(2);
        root.right = new TreeNode<Integer>(3);
        root.left.left = new TreeNode<Integer>(4);
        root.left.right = new TreeNode<Integer>(5);
        root.right.left = new TreeNode<Integer>(6);
        root.right.right = new TreeNode<Integer>(7);
        printTreeInLine(root);
    }
}
  1. 32-3 之字形打印二叉树
题目要求:  
请实现一个函数按照之字形打印二叉树。即第一层从左到右打印,第二层从右到左打印,  
第三层继续从左到右,以此类推。  

解题思路:  
第k行从左到右打印,第k+1行从右到左打印,可以比较容易想到用两个栈来实现。  
另外要注意,根据是从左到右还是从右到左访问的不同,压入左右子节点的顺序也有所不同。  
package structure;
import java.util.LinkedList;
import java.util.Queue;
/**
 * Created by ryder on 2017/6/12.
 * 树节点
 */
public class TreeNode<T> {
    public T val;
    public TreeNode<T> left;
    public TreeNode<T> right;
    public TreeNode(T val){
        this.val = val;
        this.left = null;
        this.right = null;
    }
}
package chapter4;
import structure.TreeNode;
import java.util.Stack;
/**
 * Created by ryder on 2017/7/18.
 * 之字形打印二叉树
 */
public class P176_printTreeInSpecial {
    public static void printTreeInSpeical(TreeNode<Integer> root){
        if(root==null)
            return;
        Stack<TreeNode<Integer>> stack1 = new Stack<>();
        Stack<TreeNode<Integer>> stack2 = new Stack<>();
        TreeNode<Integer> temp;
        stack1.push(root);
        while(!stack1.isEmpty() || !stack2.isEmpty()){
            if(!stack1.isEmpty()) {
                while (!stack1.isEmpty()) {
                    temp = stack1.pop();
                    System.out.print(temp.val);
                    System.out.print('\t');
                    if (temp.left != null)
                        stack2.push(temp.left);
                    if (temp.right != null)
                        stack2.push(temp.right);
                }
            }
            else {
                while (!stack2.isEmpty()) {
                    temp = stack2.pop();
                    System.out.print(temp.val);
                    System.out.print('\t');
                    if (temp.right != null)
                        stack1.push(temp.right);
                    if (temp.left != null)
                        stack1.push(temp.left);
                }
            }
            System.out.println();
        }
    }
    public static void main(String[] args){
        //            1
        //          /   \
        //         2     3
        //       /  \   / \
        //      4    5 6   7
        TreeNode<Integer> root = new TreeNode<Integer>(1);
        root.left = new TreeNode<Integer>(2);
        root.right = new TreeNode<Integer>(3);
        root.left.left = new TreeNode<Integer>(4);
        root.left.right = new TreeNode<Integer>(5);
        root.right.left = new TreeNode<Integer>(6);
        root.right.right = new TreeNode<Integer>(7);
        printTreeInSpeical(root);
    }
}
  1. 二叉搜索树的后序遍历
题目要求:  
输入一个整数数组,判断该数组是不是某二叉搜索树的后序遍历结果,假设输入数组的任意两个数都互不相同。  

解题思路:  
二叉搜索树的中序遍历是有序的,而此题是后序遍历。  
后序遍历可以很容易找到根节点,然后根据二叉搜索树的性质(左子树小于根节点,右子树大于根节点),  
可以将序列分为根节点的左子树部分与右子树部分,而后递归判断两个子树。  
package chapter4;
/**
 * Created by ryder on 2017/7/18.
 * 二叉搜索树的后序遍历序列
 */
public class P179_SequenceOfBST {   
    public static boolean verifySquenceOfBST(int[] data){
        //空树
        if(data==null||data.length==0)
            return false;
        return verifySquenceOfBST(data,0,data.length-1);
    }
    public static boolean verifySquenceOfBST(int[] data,int start,int end){
        //数组长度为2,一定能够组成BST
        if(end-start<=1)
            return true;
        int root = data[end];
        int rightStart =0;
        for(int i=0;i<end;i++){
            if(data[i]>root){
                rightStart = i;
                break;
            }
        }
        for(int i=rightStart;i<end;i++){
            if(data[i]<root)
                return false;
        }
        return verifySquenceOfBST(data,start,rightStart-1)&&verifySquenceOfBST(data,rightStart,end-1);
    }
    public static void main(String[] args){
        //            8
        //          /   \
        //         6     10
        //       /  \   / \
        //      5    7 9   11
        int[] data = {5,7,6,9,4,10,8};
        int[] data1 = {5,7,6,9,11,10,8};
        System.out.println(verifySquenceOfBST(data));
        System.out.println(verifySquenceOfBST(data1));
    }
}
  1. 二叉树中和为某一值的路径
题目要求:  
输入一棵二叉树和一个整数,打印出二叉树中节点值的和为输入整数的所有路径。  
从树的根节点开始往下一直到叶节点所经过的节点形成一条路径。  

解题思路:  
需要得到所有从根节点到叶节点的路径,判断和是否为给定值。自己写一个小的二叉树,  
通过模拟上述过程,发现获取所有路径的过程与前序遍历类似。  
因此,可以对给定二叉树进行前序遍历。当被访问节点时叶节点时,判断路径和是否为  
给定值。此外,为了记录路径上的节点,需要一个数组。  
package chapter4;
import structure.TreeNode;
import java.util.ArrayList;
import java.util.List;

/**
 * Created by ryder on 2017/7/18.
 * 二叉树中和为某一值的路径
 */
public class P182_FindPath {
    //用类似于前序遍历的思路解决
    public static void findPath(TreeNode<Integer> root,int exceptedSum){
        if(root==null)
            return;
        List<Integer> path = new ArrayList<>();
        findPath(root,path,exceptedSum,0);
    }
    //curNode为将要被访问的节点,还未被加入到path中
    public static void findPath(TreeNode<Integer> curNode,List<Integer> path,int exceptedSum,int currentSum){
        path.add(curNode.val);
        currentSum+=curNode.val;
        if(curNode.left!=null)
            findPath(curNode.left,path,exceptedSum,currentSum);
        if(curNode.right!=null)
            findPath(curNode.right,path,exceptedSum,currentSum);
        //从右子树返回当前结点,先判断是否path和为exceptedSum,是就打印path
        if(curNode.left==null && curNode.right==null && currentSum==exceptedSum)
            System.out.println(path);
        //不是就把当前结点移除
        path.remove(path.size()-1) ;
    }
   
    public static void main(String[] args) {
        //            10
        //          /   \
        //         5     12
        //       /  \
        //      4    7
        TreeNode<Integer> root = new TreeNode<Integer>(10);
        root.left = new TreeNode<Integer>(5);
        root.right = new TreeNode<Integer>(12);
        root.left.left = new TreeNode<Integer>(4);
        root.left.right = new TreeNode<Integer>(7);
        findPath(root,22);
        findPath2(root,22);
    }
}

如果二叉树的所有节点值都是大于0的(原题中并没有这个条件),可以进行剪枝。

//如果所有节点值均大于0,可进行剪枝
    public static void findPath2(TreeNode<Integer> root,int exceptedSum){
        if(root==null)
            return;
        List<Integer> path = new ArrayList<>();
        findPath2(root,path,exceptedSum,0);
    }
     //curNode为将要被访问的节点,还未被加入到path中
    public static void findPath2(TreeNode<Integer> curNode,List<Integer> path,int exceptedSum,int currentSum){
        path.add(curNode.val);
        currentSum+=curNode.val;
        //只有当currentSum小于exceptedSum时需要继续当前节点的子节点的遍历
        if(currentSum<exceptedSum){
            if(curNode.left!=null)
                findPath2(curNode.left,path,exceptedSum,currentSum);
            if(curNode.right!=null)
                findPath2(curNode.right,path,exceptedSum,currentSum);
        }
        //currentSum大于等于exceptedSum时可以直接停止当前分支的遍历,因为当前分支下currentSum只会越来越大,不会再有符合要求的解
        else if(currentSum==exceptedSum && curNode.left==null && curNode.right==null)
            System.out.println(path);
        path.remove(path.size()-1) ;
    }
  1. 复杂链表的复制
题目要求:在复杂链表中,每个节点除了有一个next指针指向下一个节点,还有一个random  
指针指向链表中的任意节点或null,请完成一个能够复制复杂链表的函数。  

解题思路:  
此题定义了一种新的数据结构,复杂链表。与传统链表的区别是多了一个random指针。本题的  
关键点也就在如何高效地完成random指针的复制。  

解法    时间复杂度   空间复杂度
解法一   o(n^2)      o(1)
解法二   o(n)        o(n)
解法三   o(n)        o(1)

/*解法一比较直接:
先把这个复杂链表当做传统链表对待,只复制val域与next域(时间o(n)),再从新链表的头部开始,
对random域赋值(时间o(n^2))。*/

package chapter4;
import java.util.HashMap;

/**
 * Created by ryder on 2017/7/18.
 * 复制复杂链表
 */
public class P187_CopyComplexList {
    public static class ComplexListNode{
        int val;
        ComplexListNode next;
        ComplexListNode random;

        public ComplexListNode(int val) {
            this.val = val;
        }
        @Override
        public String toString() {
            StringBuilder ret = new StringBuilder();
            ComplexListNode cur = this;
            while(cur!=null) {
                ret.append(cur.val);
                if(cur.random!=null)
                    ret.append("("+cur.random.val+")");
                else{
                    ret.append("(_)");
                }
                ret.append('\t');
                cur = cur.next;
            }
            return ret.toString();
        }
    }
    //解法一
    //time:o(n^2) space:o(1) 新链表使用的n个长度的空间不算入
    //先复制val与next(时间o(n)),再复制random域(时间o(n^2))
    public static ComplexListNode clone1(ComplexListNode head){
        if(head==null)
            return null;
        //需要newHead作为头,需要newCurPrev去指向newCur
        ComplexListNode newHead = new ComplexListNode(head.val);
        ComplexListNode cur = head.next;
        ComplexListNode newCur = null;
        ComplexListNode newCurPrev = newHead;
        while (cur!=null){
            newCur = new ComplexListNode(cur.val);
            newCurPrev.next = newCur;
            newCurPrev = newCurPrev.next;
            cur = cur.next;
        }
        cur = head;
        newCur = newHead;
        ComplexListNode temp = head;
        ComplexListNode newTemp = newHead;
        while(cur!=null){
            if(cur.random!=null){
                temp = head;
                newTemp = newHead;
                //重点在这,temp和newTemp一直往后面找,找到cur的random指向结点
                while (temp!=cur.random){
                    temp = temp.next;
                    newTemp = newTemp.next;
                }
                newCur.random = newTemp;
            }
            cur = cur.next;
            newCur = newCur.next;
        }
        return newHead;
    }
    public static void main(String[] args){
        ComplexListNode head = new ComplexListNode(1);
        ComplexListNode c2 = new ComplexListNode(2);
        ComplexListNode c3 = new ComplexListNode(3);
        ComplexListNode c4 = new ComplexListNode(4);
        ComplexListNode c5 = new ComplexListNode(5);
        head.next = c2;
        head.random = c3;
        head.next.next = c3;
        head.next.random = c5;
        head.next.next.next = c4;
        head.next.next.next.next = c5;
        head.next.next.next.random = c2;
        System.out.println("original:"+'\t'+head);
        System.out.println("clone1:  "+'\t'+clone1(head));
        System.out.println("clone2:  "+'\t'+clone2(head));
        System.out.println("clone3:  "+'\t'+clone3(head));
    }
}

/*解法二是用空间换时间:
解法一时间复杂度高的原因在于确定random域的费时,即假设原链表第m个节点指向第k个节点,
而在新链表的第m个节点处无法直接得到第k个节点,需从头遍历。很自然的想法是用一个哈希表
记录旧链表每个节点到新链表对应节点的映射,从而可以将时间复杂度降低为o(n)。
*/
    //解法二
    //time:o(n) space:o(n)
    //使用o(n)的空间,换取了时间复杂度的降低
    public static ComplexListNode clone2(ComplexListNode head) {
        if(head==null)
            return null;
        HashMap<ComplexListNode,ComplexListNode> oldToNew = new HashMap<>();
        ComplexListNode newHead = new ComplexListNode(head.val);
        oldToNew.put(head,newHead);
        ComplexListNode cur = head.next;
        ComplexListNode newCur = null;
        ComplexListNode newCurPrev = newHead;
        while (cur!=null){
            newCur = new ComplexListNode(cur.val);
            //保存新旧链表结点对应关系
            oldToNew.put(cur,newCur);
            newCurPrev.next = newCur;
            newCurPrev = newCurPrev.next;
            cur = cur.next;
        }
        cur = head;
        newCur = newHead;
        while(cur!=null){
            if(cur.random!=null){
                newCur.random = oldToNew.get(cur.random);
            }
            cur = cur.next;
            newCur = newCur.next;
        }
        return newHead;
    }

/*解法三:
思路很巧妙。将复制的任务分为如下三个部分:
1)cloneNodes完成新链表节点的创建,仅对val域赋值,且每个新节点接在原链表对应节点的后面。
如A->B->C,处理完后为A->A'->B->B'->C->C',时间复杂度o(n)。
2)connectRandomNode完成random域的赋值。假设A.random=C,我们需要设置A'.random=C',此处
获取C'可以在o(1)的时间复杂度完成,全部赋值完毕时间复杂度为o(n)。
3)reconnectNodes就是将上述链表重组,使A->A'->B->B'->C->C'变为A->B->C,A'->B'->C'。
此处需要注意尾部null的处理。*/

    //解法三
    //time:o(n) space:o(1)
    public static ComplexListNode clone3(ComplexListNode head) {
        if(head==null)
            return null;
        cloneNodes(head);
        connectRandomNodes(head);
        return reconnectNodes(head);
    }
    public static void cloneNodes(ComplexListNode head){
        ComplexListNode cur = head;
        ComplexListNode temp = null;
        while (cur!=null){
            temp = new ComplexListNode(cur.val);
            temp.next = cur.next;
            cur.next = temp;
            cur = cur.next.next;
        }
    }
    public static void connectRandomNodes(ComplexListNode head){
        ComplexListNode cur = head;
        ComplexListNode curNext = head.next;
        while (true){
            if(cur.random!=null)
                curNext.random = cur.random.next;
            cur = cur.next.next;
            if(cur == null)
                break;
            curNext = curNext.next.next;
        }
    }
    public static ComplexListNode reconnectNodes(ComplexListNode head){
        ComplexListNode newHead = head.next;
        ComplexListNode cur = head;
        ComplexListNode newCur = newHead;
        while (true){
            cur.next = cur.next.next;
            cur = cur.next;
            if(cur==null){
                newCur.next = null;
                break;
            }
            newCur.next = newCur.next.next;
            newCur = newCur.next;
        }
        return newHead;
    }
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 217,406评论 6 503
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,732评论 3 393
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 163,711评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,380评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,432评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,301评论 1 301
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,145评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,008评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,443评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,649评论 3 334
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,795评论 1 347
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,501评论 5 345
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,119评论 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,731评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,865评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,899评论 2 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,724评论 2 354