剑指offer刷题笔记

剑指offer刷题笔记

文章目录

  • 位运算
  • 4 解决面试题的思路
    • 27 二叉树的镜像
    • 28 对称的二叉树
    • 29 顺时针打印矩阵
    • 30 包含min函数的栈
    • 31 栈的压入、弹出序列
    • 33 二叉搜索树的后序遍历序列
    • 34 二叉树中和为某一值的路径
    • >>> 4.4 分解让复杂问题简单化
    • 36 二叉搜索树与双向链表
    • * 38 字符串的排列
  • 5 优化时间和空间效率
    • 39 数组中出现次数超过一半的数字
    • 40 最小的K个数
    • 41 数据流中的中位数
    • 42 连续子数组的最大和
    • * 43 整数中1出现的次数(从1到n整数中1出现的次数)
    • 45 把数组排成最小的数
    • 49 丑数
    • 第一个只出现一次的字符
    • * 51 数组中的逆序对
    • 52 两个链表的第一个公共结点
    • 53 数字在排序数组中出现的次数
      • 扩展题目
    • 二叉树的深度
    • 平衡二叉树
    • 57 和为S的两个数字
    • 57_2 和为S的连续正数序列
    • 58 翻转单词顺序列
    • 58_2 左旋转字符串
    • 61 扑克牌顺子
    • * 62 圆圈中最后剩下的数


位运算

n&1 : 判断n是否为奇数
因为n为奇数时,对应的二进制数最低位一定为1,n&1的结果就是1
n为偶数时,相应的最低位为0,n&1的结果就是0,
可以写n&1 ==1 或者写 n%2 == 1 或者写 n%2\

>>1 等价于 /2
<<1 等价于 *2

4 解决面试题的思路

27 二叉树的镜像

二叉树的镜像 (牛客网)

操作给定的二叉树,将其变换为源二叉树的镜像。
    /**
    * 思路:(递归)交换每个树的左右子树
    **/
    public void Mirror(TreeNode tree) {
   		if(tree == null){
			System.out.println(); 
		}
		else if(tree.left == null && tree.right ==null){
			System.out.println(); 
		}else{
            TreeNode left = tree.left;
    		tree.left = tree.right;
    		tree.right = left;
    		if(tree.left != null){
    			Mirror(tree.left);
    		}
    		if(tree.right!=null){
    			Mirror(tree.right);
    		}
    		System.out.println(tree);   
        }
    }

28 对称的二叉树

对称的二叉树

思路:(递归)
对称二叉树从左至右遍历的值,和从右至左遍历的值是相等的。

注意:某一棵子树只有左子树或只有右子树的情况,所以要记录空节点。

/*思路:首先根节点以及其左右子树,左子树的左子树和右子树的右子树相同
* 左子树的右子树和右子树的左子树相同即可,采用递归
* 非递归也可,采用栈或队列存取各级子树根节点
*/
    boolean isSymmetrical(TreeNode root){
		return isSymmetrical(root,root);
	}
    boolean isSymmetrical(TreeNode tree1,TreeNode tree2){
		if(tree1 == null && tree2 == null){
			return true;
		}
		if((tree1 == null || tree2 == null) || (tree1.val != tree2.val)){
			return false;
		}	
		return isSymmetrical(tree1.left,tree2.right) && isSymmetrical(tree1.right, tree2.left);
	}

29 顺时针打印矩阵

顺时针打印矩阵

注意:两种情况,从右至左可以的前提是up<=down,从下至上的前提是left<=right

import java.util.ArrayList;
public class Solution {
   public static ArrayList<Integer> printMatrix(int [][] matrix) {
    	ArrayList<Integer> result = new ArrayList<Integer>();
    	if(matrix.length==0){
    		return result;
    	}
    	int n = matrix.length,m = matrix[0].length;
    	int left = 0,up =0,right = m-1,down = n-1;
    	while(right>=0 && down>=0 && left <= right && up <= down){
    		// left - right
    		for(int i=left;i<=right;i++){
    			result.add(matrix[up][i]);
    		}
    		up++;
    		// up - down
    		if(up<=down){ 
	    		for(int i=up;i<=down;i++){
	    			result.add(matrix[i][right]);
	    		}
    		}
    		right--;
    		// right - left
    		if(left<=right && up<= down){
	    		for(int i=right;i>=left;i--){
	    			result.add(matrix[down][i]);
	    		}
    		}
    		down--;
    		// down - up
    		if(up<=down && left <=right){
	    		for(int i=down;i>=up;i--){
	    			result.add(matrix[i][left]);
	    		}
    		}
    		left++;
    	}

    	return result;
    }
}

30 包含min函数的栈

包含min函数的栈

定义栈的数据结构,请在该类型中实现一个能够得到栈中所含最小元素的min函数(时间复杂度应为O(1))。

思路:需要一个辅助栈tmp,里面存储每次push进来的最小值,初始值是第一个。如果后面遇到比当前tmp栈顶元素更小的值,则继续push进去。
tmp的栈顶元素就是当前的最小值。

import java.util.Stack;
public class Solution {
	Stack<Integer> s = new Stack<>();
	Stack<Integer> tmpStack = new Stack<>();
    public  void push(int node) {
        s.push(node);
        if((!tmpStack.isEmpty()&& node < tmpStack.peek()) || tmpStack.isEmpty()){
        	tmpStack.push(node);
        }
    }
    public  void pop() {
        int out = s.pop();
        if(out == tmpStack.peek()){
        	tmpStack.pop();
        }	
    }
    public  int top() {
        return s.peek();
    }
    public  int min() {
        if(tmpStack.size()>0){
        	return tmpStack.peek();
        }
        return -1;
    }
}

31 栈的压入、弹出序列

栈的压入、弹出序列

思路:
以出栈序列的第一个数字first为界限,把first前的数据进栈sdatafirst后的数据放到一个队列squeue中。
根据入栈、出栈的规律,如果下一个数字datasdata栈顶元素,则直接弹出。
如果data在剩下准备入栈的数据队列squeue中,则把剩下的数字push进栈,直到找到data

比如:
pushA = {1,2,3,4,5};
popA = {4,5,3,1,2};
那么,first是 4

sdata

sdata
3
2
1

squeue

squeue
4
5
    public static boolean IsPopOrder(int [] pushA,int [] popA) {
        Stack<Integer> sdata = new Stack<Integer>();
        Queue<Integer> squeue = new LinkedList<>();
        int first = popA[0];
        int tmp = 0;
        for(int i=0;i<pushA.length;i++){
        	if(pushA[i]==first) tmp=1;
        	if(tmp ==1)squeue.add(pushA[i]);
        	else sdata.add(pushA[i]);
        }
        boolean result = false;
        for(int i=0;i<popA.length;i++){
        	int data = popA[i];
        	if(sdata.size()>0 && sdata.peek()==data){
        		sdata.pop();result = true;
        	}else if(!squeue.isEmpty()){
        		while(!squeue.isEmpty()){
        			int q = squeue.poll();
        			if(q==data){
        				result = true;break;
        			}
        			else result =false;sdata.add(q);
        		}
        	}// else
        	else { 	result = false;break; }
        }// for
//    	System.out.println(sdata);
//    	System.out.println(squeue);
    	return result;
    }

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

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

思路:二叉搜索树,左子树节点的值都小于根节点root,右子树节点的值都大于跟节点root。\

  1. 先划分出左子树和右子树
  2. 分别判断左子树和右子树是不是二叉搜索树
  3. 注意情况:空树、不符合二叉搜索树定义的情况(右子树存在比根节点大的值)
// 递归判断左右子树就可以了
    public boolean VerifySquenceOfBST(int [] sequence) {
       return VerifySquenceOfBST(sequence,0,sequence.length-1);
    }
    public boolean VerifySquenceOfBST(int [] sequence,int begin,int end) {
    	if(sequence==null || sequence.length <=0){return false;}
    	int root = sequence[end];
    	int i=begin;
    	for(;i<end;i++){
    		if(sequence[i]>root) break;
    	}
    	// 确定右子树中的值都大于根节点
    	for(int j=i;j<end;j++){
    		if(sequence[j]<root) return false;
    	}
    	boolean left = true, right = true;
    	if(i>0 && i!= begin){
    		left = VerifySquenceOfBST(sequence,begin,i-1);
    	}
    	if(i<end){
    		right=VerifySquenceOfBST(sequence,i, end-1);
    	}
    	return left&&right;
    }

34 二叉树中和为某一值的路径

二叉树中和为某一值的路径

路径是从根节点到叶子节点经过的全部节点

思路:
以前序遍历的方式,从根节点出发,递归遍历左子树和右子树。

注意: paths添加 path时,要 new ArrayList来保存当前path的值,不然所有操作还是在之前的 path上的。

import java.util.ArrayList;
/**
public class TreeNode {
    int val = 0;
    TreeNode left = null;
    TreeNode right = null;
    public TreeNode(int val) {
        this.val = val;
    }
}
*/
public class Solution {
	public  ArrayList<ArrayList<Integer>> paths = new ArrayList<ArrayList<Integer>>();
	public  ArrayList<Integer> path = new ArrayList<Integer>();
	public  int curValue = 0;
    public  ArrayList<ArrayList<Integer>> FindPath(TreeNode root,int target) {
    	if(root==null || target==0){
    		return paths;
    	}
        path.add(root.val);
        curValue += root.val;
        boolean isleaf = root.left==null && root.right==null;
        if(isleaf && curValue==target){
        	paths.add(new ArrayList<Integer>(path));
        }
        if(root.left!=null){
        	FindPath( root.left,target);
        }
        if(root.right!=null){
        	FindPath( root.right,target);
        } 
    	path.remove(path.size()-1);
    	curValue -= root.val;
    	return paths;
    }
}

>>>>>>> 回到目录

>>> 4.4 分解让复杂问题简单化

36 二叉搜索树与双向链表

二叉搜索树与双向链表

思路:
按照(递归)中序遍历的顺序,让左子树节点指向父节点(即,左子树的 right指向父节点),让右子树节点指向父节点(即,右子树的left指向父节点)。
每个节点都指向前一个节点(left,父节点),每个左侧的节点都指向后一个节点(right,父节点)

注意:空节点的情况。

	TreeNode lastNode = null;
    public TreeNode Convert(TreeNode pRootOfTree) {
       if(pRootOfTree==null)
            return null;
        if(pRootOfTree.left==null&&pRootOfTree.right==null)
            return pRootOfTree;
    	ConvertTree(pRootOfTree);
    	TreeNode headNode = lastNode;
    	while(headNode.left != null){
    		headNode = headNode.left;
    	}
    	return headNode;  	
    }
    public void ConvertTree(TreeNode root) {
    	TreeNode current = root;
    	if(current.left != null){
    		ConvertTree(current.left);
    	}
    	current.left = lastNode;
    	if(lastNode != null){
    		lastNode.right = current;
    	}
    	lastNode = current;
    	if(current.right != null){
    		ConvertTree(current.right);
    	}
    }

链接:https://www.nowcoder.com/questionTerminal/947f6eb80d944a84850b0538bf0ec3a5
来源:牛客网

> 别人的一个简洁的答案
lastLeft 直接保存了最左边的一个节点

public class Solution {  //类似树的线索化,相当简洁
      TreeNode pre=null;
    TreeNode lastLeft=null;
    public TreeNode Convert(TreeNode pRootOfTree) {
        if(pRootOfTree==null){
            return null;
        }
        Convert(pRootOfTree.left);
        pRootOfTree.left=pre;
        if(pre!=null)pre.right=pRootOfTree;
        pre=pRootOfTree;
        lastLeft=lastLeft==null?pRootOfTree:lastLeft;
        Convert(pRootOfTree.right);
        return lastLeft;
    }
}

* 38 字符串的排列

字符串的排列

思路:

递归法
问题转换为先固定第一个字符,求剩余字符的排列;求剩余字符排列时跟原问题一样。
(1) 遍历出所有可能出现在第一个位置的字符(即:依次将第一个字符同后面所有字符交换);
(2) 固定第一个字符,求后面字符的排列(即:在第1步的遍历过程中,插入递归进行实现)。
需要注意的几点
(1) 先确定递归结束的条件,例如本题中可设begin == str.size() - 1;
(2) 形如 aba 或 aa 等特殊测试用例的情况,vector在进行push_back时是不考虑重复情况的,需要自行控制;
(3) 输出的排列可能不是按字典顺序排列的,可能导致无法完全通过测试用例,考虑输出前排序,或者递归之后取消复位操作。

[外链图片转存失败(img-DQKVqDKp-1562320247848)(https://github.com/kathy775/hellokiki/blob/master/img/note/nowcoder/jianOffer38.png?raw=true)]

注意:

  1. 输出要按字典排序,用TreeSet
  2. set转ArrayList
import java.util.*;
public class Solution {
 	public  Set<String> resultSet = new TreeSet<>();
    public  int begin = 0;
    public  ArrayList<String> Permutation(String str) {
        if(str==null || str.length()==0){
        	return new ArrayList<String>();
        }
        char tmp;
        char[] chArr = str.toCharArray();
        for(int i=begin;i<str.length();i++){
            swap(chArr,begin,i);
        	begin ++;
        	str = new String(chArr);
        	resultSet.add(str);
        	Permutation(str);
        	begin--;
            swap(chArr,begin,i);
        }
        return new ArrayList<String>(resultSet);
    }
    public void swap(char[] chArr,int i,int j){
    	char tmp = chArr[j];
    	chArr[j] = chArr[i];
    	chArr[i] = tmp;
    }
}

>>>>>>> 回到目录

5 优化时间和空间效率

39 数组中出现次数超过一半的数字

数组中出现次数超过一半的数字

思路:
在有序状态下,出现次数超过一半的数字一定在中位数的位置上。

  1. 方法一 快排(递归)
    利用快排的方法,得到位于 mid 的值。

  2. 方法二 统计次数
    2.1 见书P208
    满足题意的数字,它的出现次数大于其他所有数字出现次数的和。
    2.2 直接 HashMap 统计出每个数字的出现次数。只要判断 HashMap 中是否存在一个 key 对应的 value 大于长度一半。

注意
除2运算可以用n>>1

public class Solution {
    public int MoreThanHalfNum_Solution(int [] array) {
		int num = partition(array,0,array.length-1);
		return isThanHalf(array, num);
    }
    // 快排,返回位于数组中 mid 的数
	public  int partition(int [] array,int start,int end){
		int pivote = array[start];
		int mid = array.length >> 1;
		int startTmp = start, endTmp = end;
		while(start < end){
			while(start < end && array[end]>=pivote){
				end--;
			}
			array[start] = array[end];
			while(start < end && array[start]<=pivote){
				start++;
			}
			array[end] = array[start];
		}
		array[start] = pivote;
		if(start == mid){
			return array[mid];
		}else if(start<mid){
			return partition(array,start+1,endTmp);
		}else {
			return partition(array,startTmp,start-1);
		}
	}
	// 判断位于 mid 的数的出现次数是否超过一半
	public  int isThanHalf(int [] array,int num){
		int count = 0;
		for(int i : array){
			if(i == num) count++;
		}
		if(count > (array.length>>1)) return num;
		else return 0;
	}
}

40 最小的K个数

最小的K个数

还是用快排的思想

import java.util.ArrayList;
public class Solution {
    public ArrayList<Integer> GetLeastNumbers_Solution(int [] input, int k) {    
        ArrayList<Integer> result = new ArrayList<Integer>();
        if(k>input.length || input.length==0) return result;
        int[] a = partition(input,0,input.length-1,k);        
        for(int i=0;i<k;i++){
            result.add(a[i]);
        }
        return result;
    }
 	public int[] partition(int [] array,int start,int end,int k){
         if(k>array.length-1) k=array.length-1;
		int pivote = array[start];
		int startTmp = start, endTmp = end;
		while(start < end){
			while(start < end && array[end]>=pivote){
				end--;
			}
			array[start] = array[end];
			while(start < end && array[start]<=pivote){
				start++;
			}
			array[end] = array[start];
		}
		array[start] = pivote;
		if(start == k){
			return array;
		}else if(start<k){
			return partition(array,start+1,endTmp,k);
		}else {
			return partition(array,startTmp,start-1,k);
		}
       
	}   
}

41 数据流中的中位数

还是用快排也可以

42 连续子数组的最大和

每个数加上前面的数(>本身,则替代;反之,保留)
输出最大值即可

* 43 整数中1出现的次数(从1到n整数中1出现的次数)

整数中1出现的次数

思路:

https://www.nowcoder.com/questionTerminal/bd7f978302044eee894445e244c7eee6

设N = abcde ,其中abcde分别为十进制中各位上的数字。
如果要计算百位上1出现的次数,它要受到3方面的影响:百位上的数字,百位以下(低位)的数字,百位以上(高位)的数字。\

① 如果百位上数字为0,百位上可能出现1的次数由更高位决定。比如:12013,则可以知道百位出现1的情况可能是:100199,11001199,21002199,,…,1110011199,一共1200个。可以看出是由更高位数字(12)决定,并且等于更高位数字(12)乘以 当前位数(100)。
② 如果百位上数字为1,百位上可能出现1的次数不仅受更高位影响还受低位影响。比如:12113,则可以知道百位受高位影响出现的情况是:100199,11001199,21002199,,…,1110011199,一共1200个。和上面情况一样,并且等于更高位数字(12)乘以 当前位数(100)。但同时它还受低位影响,百位出现1的情况是:12100~12113,一共114个,等于低位数字(113)+1。
③ 如果百位上数字大于1(29),则百位上出现1的情况仅由更高位决定,比如12213,则百位出现1的情况是:100199,11001199,21002199,…,1110011199,1210012199,一共有1300个,并且等于更高位数字+1(12+1)乘以当前位数(100)。

思路理解
主要通过当前位来看,位有:个位、十位、百位…
比如,123,则个位是3,十位是2,百位是1
在个位的时候,个位可能出现1的情况,要看十位和百位。
比如有:1、(11,21…91)、101、111、121
(12 + 1)*1
在十位的时候,十位可能出现1的情况。

比如110
当前位是1时,比如十位是1
low+1,是指10 … 1? (这个大小等于low),+1 是指10的时候十位的1
high*i ,110 … 119 这段数字十位上是1

public class Solution {
    public int NumberOf1Between1AndN_Solution(int n) {
    	int low=0,high=0,cur=0,i=1,count=0;
		while(n/i != 0){
			low = n-(n/i)*i;
			high = n/(i*10);
			cur = n/i%10;
			if(cur == 0){
				count += high*i;
			}else if(cur == 1){
				count += low+1+high*i;
			}else{
				count += (high+1)*i;
			}
			i = i*10;
		}
		return count;	
    }
}

45 把数组排成最小的数

把数组排成最小的数

思路
对数组进行排序(自定义一个比较大小的函数,比较两个字符串s1, s2大小的时候,先将它们拼接起来,比较s1+s2,和s2+s1那个大,如果s1+s2大,那说明s2应该放前面,所以按这个规则,s2就应该排在s1前面)
把排序后的数组按顺序拼接起来,就是答案。

	public String PrintMinNumber(int [] numbers) {
		for(int i=0;i<numbers.length;i++){
			for(int j=i+1;j<numbers.length;j++){
				int num1 = Integer.parseInt(numbers[i]+""+numbers[j]);
				int num2 = Integer.parseInt(numbers[j]+""+numbers[i]);
				if(num1 > num2){
					int tmp = numbers[i];
					numbers[i] = numbers[j];
					numbers[j] = tmp;
				}
			}
		}
		String result = "";
		for(int i : numbers){
			result += i;
		}
		return result;
	}	

49 丑数

丑数

参考别人的代码,这个思路比较清晰,看上去直观一点。
1 2 3 4 5 6 [7] 8 9 10 [11] 12

    public static int GetUglyNumber_Solution(int index) {
    	if(index < 7)return index;
    	int[] res = new int[index];
    	res[0]=1;
    	int t2=0,t3=0,t5=0;
    	for(int i=1;i<index;i++){
    		res[i] = Math.min(res[t2]*2, Math.min(res[t3]*3, res[t5]*5));
    		if(res[i] == res[t2]*2) t2++;
    		if(res[i] == res[t3]*3) t3++;
    		if(res[i] == res[t5]*5) t5++;
    	}
        return res[index-1];
    }

第一个只出现一次的字符

public class Solution {
    public int FirstNotRepeatingChar(String str) {
    	for(int i=0;i<str.length();i++){
    		if(str.indexOf(str.charAt(i)) ==str.lastIndexOf(str.charAt(i))) 
    			return i;
    	}
        return -1;
    }
}

* 51 数组中的逆序对

public class Solution {
    public int InversePairs(int [] array) {
       if(array.length <=0) return 0;
       int[] copy = new int[array.length]; 
       for(int i=0;i<array.length;i++) copy[i] = array[i];
       int count = mergeCount(array,copy,0,array.length-1);
       return count%1000000007;
    }	
    public int mergeCount(int [] array,int [] copy,int start,int end) {
    	if(start==end){
    		copy[start]=array[start];
    		return 0;
    	}
    	int mid = (end-start)/2;
    	int left = mergeCount(copy,array,start,start+mid);
    	int right = mergeCount(copy,array,start+mid+1,end);
    	
    	int i = start+mid;
    	int j = end,index = end;
    	int count = 0;
    	while(i>=start && j>=start+mid+1){
    		if(array[i] > array[j]){
    			copy[index--]= array[i--];
    			count += j-start-mid;  
    		}else copy[index--]= array[j--];
    	}
    	for(;i>=start;i--)copy[index--]= array[i];
    	for(;j>=start+mid+1;j--)copy[index--]= array[j];
    	return count+left+right;
    }
}

52 两个链表的第一个公共结点

两个链表的第一个公共结点

    public ListNode FindFirstCommonNode(ListNode pHead1, ListNode pHead2) {
    	ListNode p1 = pHead1;
    	ListNode p2 = pHead2;
    	while(p1 != p2){
    		p1 = p1==null?pHead2:p1.next;
    		p2 = p2==null?pHead1:p2.next;
    	}
    	return p1;
    }

53 数字在排序数组中出现的次数

思路:
(方法1)因为data中都是整数,所以可以稍微变一下,不是搜索k的两个位置,而是搜索k-0.5和k+0.5
这两个数应该插入的位置,然后相减即可。
(参考别人的)

(方法2)用二分法找到数字k第一次出现的位置和最后一次出现的位置,相减。

    public int GetNumberOfK(int [] array , int k) {
        return binarySearch(array,k+0.5)-binarySearch(array,k-0.5);
    }
    public int binarySearch(int [] array , double k) {
        int start = 0,end = array.length-1;
    	int mid = 0;
    	while(start<=end){ // 注意这里的条件
    		mid = (end-start)/2+start;
    		if(array[mid]>k) end = mid-1;
            else if(array[mid]<k) start = mid+1;
    	}
    	return start;
    }

扩展题目

0~n-1 中缺失的数字

思路:每个未缺失的数字,它的下标和值本身相等。通过二分法找到下标和值不相等的数字。

二叉树的深度

思路:
方法一:递归,直接比较左右子树的高度
方法二:层次遍历得到高度

    public int TreeDepth(TreeNode root) {
        return root==null?0:(Math.max(TreeDepth(root.left),TreeDepth(root.right))+1);
    }

平衡二叉树

参考的思路:
如果改为从下往上遍历,如果子树是平衡二叉树,则返回子树的高度;如果发现子树不是平衡二叉树,则直接停止遍历,这样至多只对每个结点访问一次。

public class Solution {
    public boolean IsBalanced_Solution(TreeNode root) {
        return getHeight(root)!=-1;
    }
    public int getHeight(TreeNode root) {
        if(root==null)return 0;
        int left = getHeight(root.left);
        if(left == -1) return -1;
        int right = getHeight(root.right);
        if(right == -1) return -1;
        return Math.abs(left-right)>1?-1:Math.max(left,right)+1;
    }
}

57 和为S的两个数字

和为S的两个数字

思路:
用两个指针start,end
如果start位置的值和end位置的值的和>sum,则end--
否则,start++

    public ArrayList<Integer> FindNumbersWithSum(int [] array,int sum) {
    	 ArrayList<Integer> result = new ArrayList<>();
        if(array==null || array.length==0||sum==0)return result;
        int start = 0,end = array.length-1;
        while(end >= start){
        	int tmp=array[start]+array[end];
        	if(tmp == sum){
        		result.add(array[start]);result.add(array[end]);
        		break;
        	}else if(tmp<sum)start++;
        	 else end --;
        }
        return result;
    }

57_2 和为S的连续正数序列

和为S的连续正数序列

import java.util.ArrayList;
public class Solution {
   public ArrayList<ArrayList<Integer> > FindContinuousSequence(int sum) {
    	ArrayList<ArrayList<Integer> > result = new ArrayList<>();
    	int start=1,end = 2,num=3;
    	while(start < end){
    		if(num < sum){
    			end++;num+=end;
    		}else if(num > sum){
    			num-=start;start++;
    		}else{
    			ArrayList<Integer> nums = new ArrayList<Integer>();
    			for(int i=start;i<=end;i++){
    				nums.add(i);
    			}
    			result.add(nums);
    			num-=start;start++;end++;num+=end;
    		}
    	}
    	return result;
    }	
}

58 翻转单词顺序列

翻转单词顺序列

方法一:
O(n), n是单词个数

public class Solution {
    public String ReverseSentence(String str) {
        if(str.replace(" ","").length()==0)return str;
        String[] sarr = str.split(" ");
        String result = "";
        for(int i=sarr.length-1;i>=0;i--){
            result+=sarr[i]+" ";
           // if(i!=0) result+=" ";
        }
        return result.substring(0,result.length()-1);
    }
}

方法二:
startend指针,将sarr进行交换。
再对sarr输出的字符串进行一些处理,再输出。

58_2 左旋转字符串

左旋转字符串

方法一:

public class Solution {
    public String LeftRotateString(String str,int n) {
        if(n<0 || n>str.length()) return str;
        String s1 = str.substring(0,n);
        String s2 =  str.substring(n);
        return s2+s1;
    }
}

方法二:

原理:$YX = (X^TY^T)^T$
参考:https://www.nowcoder.com/questionTerminal/12d959b108cb42b1ab72cef4d36af5ec

    string LeftRotateString(string str, int n) 
    {
      int len = str.size();
        if(len == 0) return str;
        n %= len;
        for(int i = 0, j = n - 1; i < j; ++i, --j) swap(str[i], str[j]);
        for(int i = n, j = len - 1; i < j; ++i, --j) swap(str[i], str[j]);
        for(int i = 0, j = len - 1; i < j; ++i, --j) swap(str[i], str[j]);
        return str;
    }

61 扑克牌顺子

扑克牌顺子

思路:

  1. 先排序
  2. 统计0的个数zeroNum
  3. 统计gapNum,也就是相邻数字之间的差。
    zeroNum小于gapNum,则输出false
import java.util.Arrays;
public class Solution {
   public  boolean isContinuous(int [] numbers) {
       if(numbers.length==0) return false;
    	Arrays.sort(numbers);
    	int zeroNum = 0,gapNum = 0;
    	for(int i=0;i<numbers.length && numbers[i]==0;i++) zeroNum++;
    	int start=zeroNum,end=start+1;
    	// 为什么是 start=zeroNum?
    	// 因为数组前面的0不用统计gap
    	while(end<numbers.length){
    		if(numbers[start]==numbers[end]) return false;
    		gapNum += numbers[end]-numbers[start]-1;
    		start=end;end++;    		
    	}
    	return gapNum>zeroNum?false:true;
    }
}

* 62 圆圈中最后剩下的数

圆圈中最后剩下的数

public class Solution {
    public int LastRemaining_Solution(int n, int m) {
        if(n<1||m<1)return -1;
        int last=0;
        for(int i=2;i<=n;i++)
        	last = (last+m)%i;
        return last;      	
    }
}

你可能感兴趣的:(JAVA,算法,刷题,剑指offer刷题,算法-小题大做)