剑指offer刷题

从尾到头打印链表

链表从头到尾存入数组,数组逆序输出

(1)引入辅助数组

import java.util.ArrayList;
public class Solution {
    public ArrayList<Integer> printListFromTailToHead(ListNode listNode) {
        ArrayList<Integer> arr = new ArrayList<Integer>();
        ArrayList<Integer> arr1 = new ArrayList<Integer>();
        while(listNode!=null){
            arr.add(listNode.val);
            listNode = listNode.next;
        }
        for(int i=arr.size()-1;i>=0;i--){
            arr1.add(arr.get(i));
        }
        return arr1;
    }
}

(2)Collections的reverse方法

/**
*    public class ListNode {
*        int val;
*        ListNode next = null;
*
*        ListNode(int val) {
*            this.val = val;
*        }
*    }
*
*/
import java.util.Collections;
import java.util.ArrayList;
public class Solution {
    public ArrayList<Integer> printListFromTailToHead(ListNode listNode) {
        ArrayList<Integer> arr = new ArrayList<Integer>();
        while(listNode!=null){
            arr.add(listNode.val);
            listNode = listNode.next;
        }
        Collections.reverse(arr);
        return arr;
    }
}

递归

import java.util.ArrayList;
public class Solution {
    ArrayList<Integer> arr = new ArrayList<Integer>();
    public ArrayList<Integer> printListFromTailToHead(ListNode listNode) {
       if(listNode !=null) {
            printListFromTailToHead(listNode.next);
            arr.add(listNode.val);
        }
        return arr;
    }
}

import java.util.ArrayList;
import java.util.Stack;
public class Solution {
    public ArrayList<Integer> printListFromTailToHead(ListNode listNode) {
    	Stack<Integer> a = new Stack<Integer>();
    	while(listNode !=null) {
    		a.push(listNode.val);
    		listNode = listNode.next;
    	}
    	ArrayList<Integer> arr = new ArrayList<Integer>();
    	while(!a.isEmpty()) {
    		arr.add(a.pop());
    	}
    	return arr;
    }
}

重建二叉树

已知前序,中序,重建二叉树(假设前序和中序都不含重复数字)

我的递归

public class RecontrustBinaryTree {
    public static TreeNode reConstructBinaryTree(int [] pre,int [] in) {
        if(pre ==null || in == null)
            return null;
        if(pre.length==0 || in.length==0)
            return null ;
        int root = pre[0];
        if(pre.length == 1){
//        	System.out.println(root);
            return new TreeNode(root);
        }
        ArrayList<Integer> left = new ArrayList<Integer>();
        int j = 0;
        int count_left = 0;
        for(int i=0;i< in.length;i++){
            if(in[i]!=root) {
            	left.add(in[i]);
            	count_left++;
            }
            else{
                j= i;
                break;
            }
        }
        int count_right = 0;
        ArrayList<Integer> right = new ArrayList<Integer>();
        for(int k=j+1;k< in.length;k++){
            right.add(in[k]);  
            count_right++;
    }
        ArrayList<Integer> pre_left = new ArrayList<Integer>();
        ArrayList<Integer> pre_right = new ArrayList<Integer>();
        for(int i = 1;i<pre.length;i++) {
        	if(count_left!=0) {
        		pre_left.add(pre[i]);
        		count_left --;
        	}else {
        		pre_right.add(pre[i]);
        	}
        }
//        System.out.println(pre_left);
//        System.out.println(pre_right);
//        System.out.println(left);
//        System.out.println(right);
        int[] a = ArraytoInt(pre_left);
        int[] b = ArraytoInt(pre_right);
        int[] c = ArraytoInt(left);
        int[] d = ArraytoInt(right);
        TreeNode result = new TreeNode(root);
        result.left = reConstructBinaryTree(a,c);
        result.right = reConstructBinaryTree(b,d);
        
//        System.out.println(root);
        return result;
}
    public static void main(String[] args) {
    	int [] pre = {1,2,4,7,3,5,6,8};
    	int [] in =  {4,7,2,1,5,3,8,6};
    	TreeNode re = reConstructBinaryTree(pre,in);
    	printPostOrder(re);
	}
    public static int[] ArraytoInt(ArrayList<Integer> l) {
    	int[] res = new int[l.size()];
    	for(int i=0;i<l.size();i++) {
    		res[i] = l.get(i);
    	}
    	return res;
    }
    //递归后续遍历
    public static void printPostOrder(TreeNode root) {
        if (root != null) {
            printPostOrder(root.left);
            printPostOrder(root.right);
            System.out.println(root.val);
        }
    }  
}

大神的递归

链接:https://www.nowcoder.com/questionTerminal/8a19cbe657394eeaac2f6ea9b0f6fcf6
来源:牛客网

public class Solution {
    public TreeNode reConstructBinaryTree(int [] pre,int [] in) {
        TreeNode root=reConstructBinaryTree(pre,0,pre.length-1,in,0,in.length-1);
        return root;
    }
    //前序遍历{1,2,4,7,3,5,6,8}和中序遍历序列{4,7,2,1,5,3,8,6}
    private TreeNode reConstructBinaryTree(int [] pre,int startPre,int endPre,int [] in,int startIn,int endIn) {
         
        if(startPre>endPre||startIn>endIn)
            return null;
        TreeNode root=new TreeNode(pre[startPre]);
         
        for(int i=startIn;i<=endIn;i++)
            if(in[i]==pre[startPre]){
                root.left=reConstructBinaryTree(pre,startPre+1,startPre+i-startIn,in,startIn,i-1);
                root.right=reConstructBinaryTree(pre,i-startIn+startPre+1,endPre,in,i+1,endIn);
                      break;
            }
                 
        return root;
    }
}

栈和队列

两个栈实现队列

一个栈(如stack1)只能用来存,另一个栈(如stack2)只能用来取
当取元素时首先检查stack2是否为空,如果不空直接stack2.pop(),否则将stack1中的元素全部倒入stack2,如果倒入之后stack2仍为空则需要抛异常,否则stack2.pop()。

import java.util.Stack;
public class Solution {
    Stack<Integer> stack1 = new Stack<Integer>();
    Stack<Integer> stack2 = new Stack<Integer>(); 
    public void push(int node) {
        stack1.push(node);
    }    
    public int pop() {
        if(stack2.isEmpty()){
            while(!stack1.isEmpty()){
                stack2.push(stack1.pop());
            }
        }
        return stack2.pop();
    }
}

两个队列实现栈

leetcode225
压栈压入q1,如a,b,c.
弹栈时候,先把a,b删除并放入q2,再删去c,然后再把q2和q1交换。
取top元素:压栈的x和弹栈时候q1.poll().
判断是否为空,即判断q1

import java.util.*;
class MyStack {
    private Queue<Integer> q1;
    private Queue<Integer> q2;
    private int top;

    /** Initialize your data structure here. */
    public MyStack() {
        q1 = new LinkedList<Integer>();
        q2 = new LinkedList<Integer>();
    }    
    /** Push element x onto stack. */
    public void push(int x) {
        q1.add(x);
        top = x;
    }   
    /** Removes the element on top of the stack and returns that element. */
    public int pop() {
        while(q1.size()>1) {
        	top = q1.poll();
        	q2.add(top);
        }
        int re = q1.poll();
        Queue<Integer> temp = q1;
        q1 = q2;
        q2 = temp;
        return re;
    }
    /** Get the top element. */
    public int top() {
        return top;
    }
    /** Returns whether the stack is empty. */
    public boolean empty() {
        return q1.isEmpty();
    }
}

最小栈

leetcode
博客
leetcode题

最大栈

定义栈的数据结构,请在该类型中实现一个能够得到栈中所含最小元素的min函数(时间复杂度应为O(1))。
辅助栈每次存之前的最小元素和新压入栈的最小值

public class Solution {
    Stack<Integer> s1 = new Stack<Integer>();
    Stack<Integer> s2 = new Stack<Integer>();
	int min = Integer.MAX_VALUE;   
    public void push(int node) {
    	s1.push(node);
        if(node < min)
        	min = node;
        s2.push(min);
    }
    
    public void pop() {
        s1.pop();
        s2.pop();
    }
    
    public int top() {
        return  s1.peek();
    }
    
    public int min() {
        return s2.peek(); 
    }
}

栈的压入弹出序列

输入两个整数序列,第一个序列表示栈的压入顺序,请判断第二个序列是否可能为该栈的弹出顺序。假设压入栈的所有数字均不相等。例如序列1,2,3,4,5是某栈的压入顺序,序列4,5,3,2,1是该压栈序列对应的一个弹出序列,但4,3,5,1,2就不可能是该压栈序列的弹出序列。
分析:引入辅助栈,以栈的压入序列 1,2,3,4,5为例:
对于序列4,5,3,2,1:
先压入1,1 ≠ 4;继续压入2,2≠ 4;继续压入3,3≠ 4;继续压入4,4=4;
此时辅助栈里面是1,2,3,4,弹出4,辅助栈为 1,2,3。
接下来看辅助栈栈顶是不是等于5,不等于5,继续压栈5,此时进入while循环,3,2,1与弹出序列的顺序一样。
对于序列4,3,5,1,2,先压入1,1 ≠ 4;继续压入2,2≠ 4;继续压入3,3≠ 4;继续压入4,4=4;
此时辅助栈里面是1,2,3,4,弹出4,辅助栈为 1,2,3。
弹出3,压入5再弹出,此时辅助栈为1,2,栈顶为2.而弹出序列为1,2.
1 ≠ 2,跳出while循环及for循环,此时辅助栈不为空,所以不可能是弹出序列。

public class Solution {
    public boolean IsPopOrder(int [] pushA,int [] popA) {
    	if(pushA.length ==0 || popA.length ==0)
    		return false;
    	Stack<Integer> helper = new Stack<Integer>();
    	int index = 0;
    	for(Integer t : pushA) {
    		helper.push(t);
    		while(!helper.isEmpty() && helper.peek() == popA[index]) {
    			helper.pop();
    			index++;
    		}
    	}
    	return helper.isEmpty();
    }      
}

旋转数组

旋转数组的最小数组

按照剑指offer的思路,两个指针:类似二分查找,循环结束条件是两个指针相邻;
mid == 左 == 右,顺序查找;

public class Solution {
    public int minNumberInRotateArray(int [] array) {
        if(array == null || array.length == 0)
            return 0;
        if(array[0] < array[array.length - 1])
            return array[0];
        else if(array[0] == array[array.length - 1] && array[0] == array[(array.length - 1)/2]) {
            //顺序查找,遍历
            int min = Integer.MAX_VALUE;
            for (int i = 0; i < array.length; i++) {
                if(array[i] < min) {
                    min = array[i];
                }
            }
            return min;
        }
        else{
            int index1 = 0;
            int index2 = array.length - 1;
            int mid = (index1 + index2)/2;
            while(index1 != index2 - 1) {
                if(array[mid] >= array[index1]) {
                    index1 = mid;
                }else {
                    index2 = mid;
                }
                mid = (index1 + index2)/2;
            }
            return array[index2];
        }
    }
}

牛客上其他人的做法,跟二分查找比较类似

链接:https://www.nowcoder.com/questionTerminal/9f3231a991af4f55b95579b44b7a01ba
来源:牛客网

public class Solution {
    public int minNumberInRotateArray(int [] array) {
        int low = 0 ; int high = array.length - 1;   
        while(low < high){
            int mid = low + (high - low) / 2;        
            if(array[mid] > array[high]){
                low = mid + 1;
            }else if(array[mid] == array[high]){
                high = high - 1;
            }else{
                high = mid;
            }   
        }
        return array[low];
    }
}

循环升序数组,找数n

二分
注意等号

class Solution {
    public int search(int[] nums, int target) {
        int index1 = 0;
        int index2 = nums.length -1;
        while(index1 < index2) {
        	int mid = index1 + (index2 - index1)/2;
        	if(nums[mid] == target)
            	return mid;
            	//nums[index1] <= nums[mid],不能漏掉等号
            else if(nums[index1] <= nums[mid] && (target > nums[mid] || target< nums[index1])) {
            	index1 = mid + 1;
            }else if(nums[index1] > nums[mid] && target > nums[mid] && target< nums[index1]) {
            	index1 = mid + 1;
            }else {
            	index2 = mid;
            }
        }
        return index2 == index1 && nums[index1] == target ? index1 : -1;
    }
}

二分和递归:
将数组一分为二,其中一定有一个是有序的,另一个可能是有序,也能是部分有序。此时有序部分用二分法查找。无序部分再一分为二,其中一个一定有序,另一个可能有序,可能无序。就这样循环.

class Solution {
	public static int search(int[] nums, int target) {
		return search( nums, 0, nums.length  -1, target);
	}
    //递归来做
    private static int search(int[] nums, int low, int high, int target) {
    	if(low > high)
    		return -1;
    	int mid = low + (high - low)/2;
    	if(nums[mid] == target)
    		return mid;
    	if(nums[mid] < nums[high]) {
    		if(nums[mid] < target && target <= nums[high])
    			return search( nums, mid+1, high, target);
    		else
    			return search( nums, low, mid -1, target);
    	}else {
    	//nums[low] <= target 等号不要漏掉
    		if(nums[low] <= target && target < nums[mid])
    			return search( nums, low, mid -1, target);
    		else
    			return search( nums, mid +1, high, target);
    	}
    }
}

逆序/顺序 螺旋输出数组

顺时针
在这里插入图片描述

import java.util.ArrayList;
public class Solution {
    public static ArrayList<Integer> printMatrix(int [][] matrix) {
    	int rows = matrix.length ;
    	int columns = matrix[0].length;
        if(matrix == null || rows == 0 ||  columns == 0)
        	return null;
        int start = 0;
        ArrayList<Integer> res = new ArrayList<Integer>();
        while(rows > start*2 && columns > start*2) {
        	ArrayList<Integer> re = printMatrixClockwise(matrix,rows, columns,start);
        	start ++;
        	res.addAll(re);
        }
        return res;
    }	
    public static ArrayList<Integer> printMatrixClockwise(int [][] matrix,int rows,int columns,int start) {
    	ArrayList<Integer> res = new ArrayList<Integer>();
    	int endX = columns -1 - start;
    	int endY = rows -1- start;
    	//从左到右,每个圈必打印这一步
    	for (int i = start; i <= endX; i++) {
			int num = matrix[start][i];
			res.add(num);
		}
    	//第二步,从右到下,条件是终止行号大于起始行号
    	if(start < endY) {
    		for(int i = start +1;i<= endY;i++) {
    			int num = matrix[i][endX];
    			res.add(num);
    		}
    	}
    	//第三步,从右到左,条件是终止行号大于起始行号并且终止列号要大于起始列号
    	if(start < endY && start < endX) {
    		for(int i = endX-1;i>= start;i--) {
    			int num = matrix[endY][i];
    			res.add(num);
    		}
    	}
    	//第四步,从下到上,条件是必须三行两列,即终止行号比起始行号大2并且终止列号要大于起始列号
    	if(start < endY -1 && start < endX){
    		for(int i = endY-1;i>= start + 1;i--) {
    			int num = matrix[i][start];
    			res.add(num);
    		}
    	}
    	return res;
    }
}

java ArrayList 中 add 与addAll的区别
下面有两个List,我想将第二个List的数据添加到第一个List当中,用add方法的话是这样添加的:


    for(String item : list2){

      list1.add(item);

    }
如果使用addAll的话:
    list1.addAll(list2);

也可以定义要添加的位置,可以用addAll(int index, Collection<?>);
逆时针

public class PrintMatrixAnticlockwise {
    public static ArrayList<Integer> printMatrix(int [][] matrix) {
    	int rows = matrix.length ;
    	int columns = matrix[0].length;
        if(matrix == null || rows == 0 ||  columns == 0)
        	return null;
        int start = 0;
        ArrayList<Integer> res = new ArrayList<Integer>();
        while(rows > start*2 && columns > start*2) {
        	ArrayList<Integer> re = printMatrixClockanti(matrix,rows, columns,start);
        	start ++;
        	res.addAll(re);
        }
        return res;
    }	
    public static ArrayList<Integer> printMatrixClockanti(int [][] matrix,int rows,int columns,int start) {
    	ArrayList<Integer> res = new ArrayList<Integer>();
    	int endX = columns -1 - start;
    	int endY = rows -1- start;
    	//从上到下,每个圈必打印这一步
    	for(int i = start;i<= endY;i++) {
			int num = matrix[i][start];
			res.add(num);
		}
    	if(start < endX){
    	//从左到右
    	for (int i = start+1; i <= endX; i++) {
			int num = matrix[endY][i];
			res.add(num);
		}
    	}
    	//从下到上
    	if(start < endY && start < endX) {
    		for(int i = endY-1;i>= start;i--) {
    			int num = matrix[i][endX];
    			res.add(num);
    		}
    	}
    	//从右到左
    	if(start < endY  && start < endX -1){
    		for(int i = endX-1;i>= start +1;i--) {
    			int num = matrix[start][i];
    			res.add(num);
    		}
    	}
    	return res;
    }
    }

数值的整数次方

  • 1.全面考察指数的正负、底数是否为零等情况。
  • 2.写出指数的二进制表达,例如13表达为二进制1101。
  • 3.优化:右移代表除以2,位运算代替取余
    递归
public class Solution {
    public  double Power(double base, int exponent) {   
        if(base == 0 && exponent< 0) 
            throw  new RuntimeException();
        if(base == 0)
        	return 0;
        if(base == 1 || exponent == 0)
        	return 1;
        int n = exponent;
        if(exponent < 0) 
        	n = - exponent;
        double res = Power(base,n>>1);
        res *= res;
        if((n & 1) ==1)
        	res *= base;
        if(exponent < 0) 
        	return 1/res;
        else
        	return res;
  }
}

不用递归,直接判断

    public  double Power(double base, int exponent) {   
        if(base == 0 && exponent< 0) 
            throw  new RuntimeException();
        if(base == 0)
        	return 0;
        if(base == 1 || exponent == 0)
        	return 1;
        int n = Math.abs(exponent);
        double res = 1;
        if((n & 1) ==1)
    		res = base;
        while(n != 0) {
        	n = n>>1;
            res *= base;
        }
        if(exponent < 0) 
        	return 1/res;
        else
        	return res;
  }

链表的倒数第k个结点

思想:两个指针,其中一个比另一个快k-1步
**鲁棒: 空链表或者k=0
链表长度小于k
**

    public ListNode FindKthToTail(ListNode head,int k) {
    	if(head == null || k ==0)
    		return null;
    	ListNode p1 = head;
    	ListNode p2 = head;
    	int i = 0;
    	while(p1 != null) {
    		if(i >= k)
    			p2 = p2.next;
    		p1 = p1.next;
    		i++;
    	}
    	return i>=k ? p2:null;

    }

链表

反转链表

非递归

    public ListNode ReverseList(ListNode head) {
    	if(head == null || head.next == null)
    		return head;
    	ListNode current = head;
    	ListNode pre = null;
    	ListNode ne = null;
    	while(current != null) {
    		ne = current.next;
    		current.next = pre;
    		pre = current;
    		current = ne;
    	}
    	return pre;
    }

递归

 private ListNode reverse(ListNode head){
         if (head == null || head.next == null)
             return head;
          ListNode temp = head.next;//保存下一个节点
          ListNode newHead = reverse(head.next);//整体思维,宏观语义
          temp.next = head;//连上头与递归部分
          head.next = null;//调整尾部
          return newHead;//返回头节点
    }
————————————————
版权声明:本文为CSDN博主「小胖FWC」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/u010651249/article/details/90247595

在这里插入图片描述
这是递归压栈到最后一个元素,设置一个存储变量,记录新链表的头节点(原链表的尾节点。。。)Rev_head。
然后将4弹出,现在head指向3, 代码是:head->next->next = head; head->next = NULL;

该节点head为3 head->next->next = head将4的next指向3,这里要注意,修改了指向后,原链表指向还存在,需要删除,所以有head->next = NULL; 依照这样的思路一直递归到1,返回之前记录的Rev_head
————————————————
版权声明:本文为CSDN博主「djxnsiwbfk」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/Wu_ye123/article/details/88729736

合并有序链表

这个在瓜子二面就写过,是一个白板直接敲代码
非递归

   public ListNode Merge(ListNode list1,ListNode list2) {
        if(list1 == null)
            return list2;
        if(list2 == null)
            return list1;
    	ListNode list3 =  new ListNode(-1);
        ListNode head = list3;
    	while(list1 != null && list2 != null) {
    		
    		if(list1.val <= list2.val) {
        		list3.next = list1;
        		list1 = list1.next;
        	}else {
        		list3.next = list2;
        		list2 = list2.next;
        	}
    		list3 = list3.next;
    	}
    	if(list1 != null)
    		list3.next = list1;
    	if(list2 != null) 
    		list3.next = list2;
    	return head.next;
    }

易错点
1.list3要初始化 ListNode list3 = new ListNode(-1);
2.要重新定义一个链表等于list3,因为list3的指针会一直向后移动
3.list3 = list3.next; 不要忘记,不仅仅list1和list2的指针在动,list3的指针也在动。

树的子结构

链接:https://www.nowcoder.com/questionTerminal/6e196c44c7004d15b1610b9afca8bd88
来源:牛客网

public class Solution {
/第一部分,递归看子树根节点是否相等
    public static boolean HasSubtree(TreeNode root1, TreeNode root2) {
        boolean result = false;
        //当Tree1和Tree2都不为零的时候,才进行比较。否则直接返回false
        if (root2 != null && root1 != null) {
            //如果找到了对应Tree2的根节点的点
            if(root1.val == root2.val){
                //以这个根节点为为起点判断是否包含Tree2
                result = doesTree1HaveTree2(root1,root2);
            }
            //如果找不到,那么就再去root的左儿子当作起点,去判断时候包含Tree2
            if (!result) {
                result = HasSubtree(root1.left,root2);
            }
             
            //如果还找不到,那么就再去root的右儿子当作起点,去判断时候包含Tree2
            if (!result) {
                result = HasSubtree(root1.right,root2);
               }
            }
            //返回结果
        return result;
    }
 //第二部分,判断左右子树是否相同
    public static boolean doesTree1HaveTree2(TreeNode node1, TreeNode node2) {
        //如果Tree2已经遍历完了都能对应的上,返回true
        if (node2 == null) {
            return true;
        }
        //如果Tree2还没有遍历完,Tree1却遍历完了。返回false
        if (node1 == null) {
            return false;
        }
        //如果其中有一个点没有对应上,返回false
        if (node1.val != node2.val) {  
                return false;
        }
         
        //如果根节点对应的上,那么就分别去子节点里面匹配
        return doesTree1HaveTree2(node1.left,node2.left) && doesTree1HaveTree2(node1.right,node2.right);
    }

二叉树的镜像

递归
二叉树的前序遍历,如果遍历到的节点有子节点,则交换它的两个子节点。

    public void Mirror(TreeNode root) {
    	if(root == null)
    		return;
    	if(root.left == null && root.right == null)
    		return;
    	TreeNode tmp = root.left;
    	root.left = root.right;
    	root.right = tmp;
    	if(root.left != null)
    		Mirror(root.left);
    	if(root.right != null)
    		Mirror(root.right);        
    }

非递归

    public void Mirror(TreeNode root) {
    	//栈
    	if(root == null)
    		return;
    	if(root.left == null && root.right == null)
    		return;
    	Stack<TreeNode> s = new Stack<>();
    	s.push(root);
    	while(s.size() >0) {
    		TreeNode p = s.pop();
    		TreeNode tmp = p.left;
        	p.left = p.right;
        	p.right = tmp;
        	if(p.left != null)
        		s.push(p.left);
        	if(p.right != null)
        		s.push(p.right);
    	}
    }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值