public int lastRemaining(int n, int m) {
构造list
int idx = 0;
while (n > 1) {
idx = (idx + m - 1) % n;
list.remove(idx);
n--;
}
return list.get(0);
}
public int lastRemaining(int n, int m) {
int ans=0; 最后的下标必然等于0
for (int i=2;i<=n;i++){
ans=(ans+m)%i; i是倒数最后一次删除前的数据size
}
return ans;
}
输入一个字符串,打印出该字符串中字符的所有排列。
你可以以任意顺序返回这个字符串数组,但里面不能有重复元素。
示例:
输入:s = “abc”
输出:[“abc”,“acb”,“bac”,“bca”,“cab”,“cba”]
题解链接.
class Solution {
List<String> res = new LinkedList<>();
char[] c;
public String[] permutation(String s) {
c = s.toCharArray();
dfs(0);
return res.toArray(new String[res.size()]);
}
void dfs(int x) {
if(x == c.length - 1) {
res.add(String.valueOf(c)); 添加排列方案
return;
}
HashSet<Character> set = new HashSet<>();
for(int i = x; i < c.length; i++) {
if(set.contains(c[i])) continue; 重复,因此剪枝
set.add(c[i]);
swap(i, x); 交换,将 c[i] 固定在第 x 位
dfs(x + 1); 开启固定第 x + 1 位字符
swap(i, x); 恢复交换
}
}
void swap(int a, int b) {
char tmp = c[a];
c[a] = c[b];
c[b] = tmp;
}
}
public ListNode reverseList(ListNode head) {
ListNode pre=null,nxt; 定义pre和nxt
while(head!=null){
nxt=head.next; 保存下一节点
head.next=pre; 改变cur.next指向
pre=head; 更新pre为cur
head=nxt; 更新cur为nxt
}
return pre;
}
class Solution {
public ListNode reverseList(ListNode head) {
//递归终止条件是当前为空,或者下一个节点为空
if(head==null || head.next==null) {
return head;
}
//这里的cur就是最后一个节点
ListNode cur = reverseList(head.next);
//这里请配合动画演示理解
//如果链表是 1->2->3->4->5,那么此时的cur就是5
//而head是4,head的下一个是5,下下一个是空
//所以head.next.next 就是5->4
head.next.next = head;
//防止链表循环,需要将head.next设置为空
head.next = null;
//每层递归函数都返回cur,也就是最后一个节点
return cur;
}
}
在数组中的两个数字,如果前面一个数字大于后面的数字,则这两个数字组成一个逆序对。输入一个数组,求出这个数组中的逆序对的总数。
输入: [7,5,6,4]
输出: 5
两层循环暴力比较并计数,面试官会问你优化
https://leetcode-cn.com/problems/shu-zu-zhong-de-ni-xu-dui-lcof/solution/shu-zu-zhong-de-ni-xu-dui-by-leetcode-solution/
class Solution {
public int reversePairs(int[] nums) {
if (nums.length<2){
return 0;
}
int[] tmp=new int[nums.length];
求区间0-nums.length的数组的逆序对
int ans=reversePairs(nums,0,nums.length-1,tmp);
nums已经被排好序,如果不能改变原数组,则需要开始时候拷贝
for(int i:nums){
System.out.print(i+" ");
}
return ans;
}
求区间0-nums.length的数组的逆序对,
前提:左边排好序,右边排好序,
private int reversePairs(int[] nums,int left,int right,int[] tmp){
if (left==right){
return 0;
}
递归中的递:不断分解子问题
int mid=left+(right-left)/2;
int leftNum=reversePairs(nums,left,mid,tmp);
int rightNum=reversePairs(nums,mid+1,right,tmp);
递归中的归:合并时累加逆序对
优化:左边最大值<右边最小值:合并过程中不产生交叉的逆序对
if (nums[mid]<nums[mid+1]){
return leftNum+rightNum;
}
int crossNum=mergePairs(nums,left,mid,right,tmp);
return leftNum+rightNum+crossNum;
}
合并数组时中累加逆序对数
private int mergePairs(int[] nums,int left,int mid,int right,int[] tmp){
tmp暂存,每次需要拷贝当前的数据
nums是最终归并后的数据
for(int i=left;i<=right;i++){
tmp[i]=nums[i];
}
int i=left;
int j=mid+1;
int crossCnt=0;
for (int k=left;k<=right;k++){
判断i和j的边界
if (i>=mid+1){
nums[k]=tmp[j];
j++;
}else if(j>=right+1){
nums[k]=tmp[i];
i++;
}else if(tmp[i]<=tmp[j]){
nums[k]=tmp[i];
i++;
}else{
nums[k]=tmp[j];
crossCnt+=mid-i+1;
j++;
}
}
return crossCnt;
}
}
一个整型数组 nums 里除两个数字之外,其他数字都出现了两次。请写程序找出这两个只出现一次的数字。要求时间复杂度是O(n),空间复杂度是O(1)。
示例 1:
输入:nums = [4,1,4,6]
输出:[1,6] 或 [6,1]
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/shu-zu-zhong-shu-zi-chu-xian-de-ci-shu-lcof
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
class Solution {
public int[] singleNumbers(int[] nums) {
int xor=0;
for (int i:nums){
xor^=i;
}
int mask=1;
while((xor&mask)==0){
mask=mask<<1;
//mask<<=1;
}
int ans1=0,ans2=0;
for (int i:nums){
if ((i&mask)==0){
ans1^=i;
}else{
ans2^=i;
}
}
return new int[]{ans1,ans2};
}
}
class CQueue {
Stack<Integer> stack1;
Stack<Integer> stack2;
public CQueue() {
this.stack1 = new Stack<>();
this.stack2 = new Stack<>();
}
public void appendTail(int value) {
stack1.add(value);
}
public int deleteHead() {
if (stack2.isEmpty()){
if (stack1.isEmpty()){
return -1;
}else{
while (!stack1.isEmpty()){
stack2.add(stack1.pop());
}
}
}
return stack2.pop();
}
}
/**
* Your CQueue object will be instantiated and called as such:
* CQueue obj = new CQueue();
* obj.appendTail(value);
* int param_2 = obj.deleteHead();
*/
示例 1:
输入:matrix = [[1,2,3],[4,5,6],[7,8,9]]
输出:[1,2,3,6,9,8,7,4,5]
class Solution {
public int[] spiralOrder(int[][] matrix) {
if(matrix.length == 0) return new int[0];
int l = 0, r = matrix[0].length - 1, t = 0, b = matrix.length - 1, x = 0;
int[] res = new int[(r + 1) * (b + 1)];
while(true) {
for(int i = l; i <= r; i++) res[x++] = matrix[t][i]; left to right.
if(++t > b) break;
for(int i = t; i <= b; i++) res[x++] = matrix[i][r]; top to bottom.
if(l > --r) break;
for(int i = r; i >= l; i--) res[x++] = matrix[b][i]; right to left.
if(t > --b) break;
for(int i = b; i >= t; i--) res[x++] = matrix[i][l]; bottom to top.
if(++l > r) break;
}
return res;
}
}
给定一个数字,我们按照如下规则把它翻译为字符串:0 翻译成 “a” ,1 翻译成 “b”,……,11 翻译成 “l”,……,25 翻译成 “z”。一个数字可能有多个翻译。请编程实现一个函数,用来计算一个数字有多少种不同的翻译方法。
示例 1:
输入: 12258
输出: 5
解释: 12258有5种不同的翻译,分别是"bccfi", “bwfi”, “bczi”, “mcfi"和"mzi”
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/ba-shu-zi-fan-yi-cheng-zi-fu-chuan-lcof
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
class Solution {
private int cnt=0;
private String numStr;
public int translateNum(int num) {
this.numStr=String.valueOf(num);
dfs(0);
return cnt;
}
private void dfs(int idx){
if (idx==numStr.length()){
cnt++;
return;
}else if(idx>numStr.length()){
return;
}
dfs(idx+1);
if (numStr.charAt(idx)=='1'){
dfs(idx+2);
}else if(numStr.charAt(idx)=='2' && idx<numStr.length()-1 && numStr.charAt(idx+1)<='5'){
dfs(idx+2);
}
}
}
class Solution {
public int translateNum(int num) {
String s = String.valueOf(num);
int[] dp = new int[s.length()+1];
dp[0] = 1;
dp[1] = 1;
for(int i = 2; i <= s.length(); i ++){
String temp = s.substring(i-2, i);
if(temp.compareTo("10") >= 0 && temp.compareTo("25") <= 0)
dp[i] = dp[i-1] + dp[i-2];
else
dp[i] = dp[i-1];
}
return dp[s.length()];
}
}
示例 2:
输入:arr = [0,1,2,1], k = 1
输出:[0]
限制:
0 <= k <= arr.length <= 10000
0 <= arr[i] <= 10000
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/zui-xiao-de-kge-shu-lcof
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
public int[] getLeastNumbers(int[] arr, int k) {
if (k == 0 || arr.length == 0) {
return new int[0];
}
统计每个数字出现的次数
int[] counter = new int[10001];
for (int num: arr) {
counter[num]++;
}
根据counter数组从头找出k个数作为返回结果
int[] res = new int[k];
int idx = 0;
for (int num = 0; num < counter.length; num++) {
while (counter[num]-- > 0 && idx < k) {
res[idx++] = num;
}
if (idx == k) {
break;
}
}
return res;
}
import java.util.*;
class Solution {
public int[] getLeastNumbers(int[] arr, int k) {
TreeMap<Integer, Integer> map = new TreeMap<>();
for (int i = 0; i < arr.length; i++) {
if (map.containsKey(arr[i])) {
map.put(arr[i], map.get(arr[i]) + 1);
} else {
map.put(arr[i], 1);
}
}
int cnt = 0;
int[] ans=new int[k];
for (Map.Entry<Integer,Integer> entry : map.entrySet()) {
int min=Math.min(k-cnt,entry.getValue());
for (int i = 0; i < min; i++) {
ans[cnt++]=entry.getKey();
}
}
return ans;
}
}
public int[] getLeastNumbers(int[] arr, int k) {
if (k == 0 || arr.length == 0) {
return new int[0];
}
默认是小根堆,实现大根堆需要重写一下比较器。
Queue<Integer> pq = new PriorityQueue<>((v1, v2) -> v2 - v1);
for (int num: arr) {
if (pq.size() < k) {
pq.add(num);
} else if (num < pq.peek()) {
pq.poll();
pq.add(num);
}
}
返回堆中的元素
int[] res = new int[pq.size()];
int idx = 0;
for(int num: pq) {
res[idx++] = num;
}
return res;
}
class MySolution {
public void quickSort(int[] arr) {
quickSort(arr,0,arr.length-1);
}
private void quickSort(int[] arr,int l,int r){
if (l<r){
int base = partition(arr, l, r);
quickSort(arr,l,base-1);
quickSort(arr,base+1,r);
}
}
private int partition(int[] arr, int l, int r){
int base =l; 定义基准值为左边的值
int idx=base+1; 定义最终返回的基准值的下标
for (int i = idx; i <=r; i++) {
if (arr[i]<arr[base]){
swap(arr,i,idx);
idx++; 下标右移,表示找到一个比基准值小的数
}
}
交换基准值与idx-1的值,使得基准值所处位置正确
swap(arr,base,idx-1);
return idx-1;
}
private void swap(int[] arr,int i,int j){
int temp=arr[i];
arr[i]=arr[j];
arr[j]=temp;
}
}
public int[] getLeastNumbers(int[] arr, int k) {
if (k == 0 || arr.length == 0) {
return new int[0];
}else if(k==arr.length){
return arr;
}
int base=quickSort(arr,0,arr.length-1,k);
return Arrays.copyOf(arr, base);
}
private int quickSort(int[] arr,int l,int r,int k){
int base =partition(arr,l,r);
if (base==k){
return base;
}
base>k 继续划分左区间,反之,继续划分右区间
return base>k? quickSort(arr,l,base-1,k):quickSort(arr,base+1,r,k);
}
private int partition(int[] arr, int left, int right) {
// 设定基准值(pivot)
int pivot = left;
int index = pivot + 1;
for (int i = index; i <= right; i++) {
if (arr[i] < arr[pivot]) {
swap(arr, i, index);
index++;
}
}
swap(arr, pivot, index - 1);
return index - 1;
}
private void swap(int[] arr, int i, int j) {
int temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}
输入: nums = [-2,1,-3,4,-1,2,1,-5,4]
输出: 6
解释: 连续子数组 [4,-1,2,1] 的和最大,为 6。
遍历下标,遍历子数组长度,遍历子数组求和:超时
public int maxSubArray(int[] nums) {
int maxSum=Integer.MIN_VALUE;
for(int i=0;i<nums.length;i++){
for(int l=1;l<=nums.length-i;l++){
int curSum=0;
for (int j = 0; j < l; j++) {
curSum+=nums[i+j];
}
maxSum=Math.max(maxSum,curSum);
}
}
return maxSum;
}
public int maxSubArray(int[] nums) {
int[] dp=new int[nums.length];
dp[0]=nums[0];
int ans=dp[0];
for(int i=1;i<nums.length;i++){
if (dp[i-1]>0){
dp[i]=dp[i-1]+nums[i];
}else{
dp[i]=nums[i];
}
ans=Math.max(ans,dp[i]);
}
return ans;
}
由于dp[i]=dp[i-1]+nums[i];
dp[i]只与dp[i-1]相关,因此可以用两个变量储存即可
public int maxSubArray(int[] nums) {
int a=nums[0];
int ans=a;
int b;
for(int i=1;i<nums.length;i++){
if (a>0){
b=a+nums[i];
}else{
b=nums[i];
}
ans=Math.max(ans,b);
这里a的值更新
a=b;
}
return ans;
}
判断条件优化,让代码更加优雅
if (a>0){
b=a+nums[i];
}else{
b=nums[i];
}
可以替换为
b=Math.max(0,a)+nums[i];
更进一步,使用原来的数组nums缓存数据,可以只使用一个int变量
public int maxSubArray(int[] nums) {
int res = nums[0];
for(int i = 1; i < nums.length; i++) {
nums[i] += Math.max(nums[i - 1], 0);
res = Math.max(res, nums[i]);
}
return res;
}
一只青蛙一次可以跳上1级台阶,也可以跳上2级台阶。求该青蛙跳上一个 n 级的台阶总共有多少种跳法。
答案需要取模 1e9+7(1000000007),如计算初始结果为:1000000008,请返回 1。
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/qing-wa-tiao-tai-jie-wen-ti-lcof
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
暴力递归深度;
public int numWays(int n) {
if (n<2){
return 1;
}
else if(n==2){
return 2;
}
return numWays(n-1)+numsWays(n-2);
}
class Solution {
private int[] arr;
public int numWays(int n) {
if (n<2){
return 1;
}
this.arr=new int[n+1];
arr[0]=1;
arr[1]=1;
arr[2]=2;
return helper(n);
}
private int helper(int n){
if(n<=2 || arr[n]!=0){
return arr[n];
}
arr[n-1]=helper(n-1)%(1000000007);
arr[n-2]=helper(n-2)%(1000000007);
arr[n]= (arr[n-1]+arr[n-2])%(1000000007);
return arr[n];
}
}
状态转移方程其实已经在递归代码中写出来了
朴素DP
public int numWays(int n) {
if (n<2){
return 1;
}
int[] dp= new int[n+1];
dp[0]=1;
dp[1]=1;
for(int i=2;i<=n;i++){
dp[i]=(dp[i-1]+dp[i-2])%(1000000007);
}
return dp[n];
}
状态压缩
class Solution {
public int numWays(int n) {
int a = 1, b = 1, sum;
for(int i = 0; i < n; i++){
sum = (a + b) % 1000000007;
a = b;
b = sum;
}
return a;
}
}
把n个骰子扔在地上,所有骰子朝上一面的点数之和为s。输入n,打印出s的所有可能的值出现的概率。
你需要用一个浮点数数组返回答案,其中第 i 个元素代表这 n 个骰子所能掷出的点数集合中第 i 小的那个的概率。
输入: 2
输出: [0.02778,0.05556,0.08333,0.11111,0.13889,0.16667,0.13889,0.11111,0.08333,0.05556,0.02778]
dp[i][j] ,表示投掷完 i枚骰子后,点数 j 的出现次数。
转移方程及边界条件
public double[] twoSum(int n) {
int[][] dp=new int[12][67];
初始化
for(int i=1;i<=6;i++){
dp[1][i]=1;
}
for(int i=2;i<=11;i++){
for(int j=i;j<=6*i;j++){
//种类求和
for(int k=1;k<=6;k++){
if(j-k<=0){
break;
}
dp[i][j]+=dp[i-1][j-k];
}
}
}
double[] ans=new double[1+5*n];
计算概率
double all=Math.pow(6,n);
for(int i=n;i<=6*n;i++){
ans[i-n]=dp[n][i]*1.0/all;
}
return ans;
}
用概率直接计算,并压缩状态
public double[] twoSum(int n) {
double pre[]={1/6d,1/6d,1/6d,1/6d,1/6d,1/6d};
for(int i=2;i<=n;i++){
double tmp[]=new double[5*i+1];
for(int j=0;j<pre.length;j++)
for(int x=0;x<6;x++)
tmp[j+x]+=pre[j]/6;
pre=tmp;
}
return pre;
}
作者:zhi-xiong
链接:https://leetcode-cn.com/problems/nge-tou-zi-de-dian-shu-lcof/solution/java-dong-tai-gui-hua-by-zhi-xiong/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
给定一个会议时间安排的数组,每个会议时间都会包括开始和结束的时间 [[s1,e1],[s2,e2],…] (si < ei),为避免会议冲突,同时要考虑充分利用会议室资源,请你计算至少需要多少间会议室,才能满足这些会议安排。
示例 1:
输入: [[0, 30],[5, 10],[15, 20]]
输出: 2
class Solution {
public int minMeetingRooms(int[][] intervals) {
if (intervals.length == 0) {
return 0;
}
Min heap
PriorityQueue<Integer> allocator =new PriorityQueue<Integer>(intervals.length,(o1,o2)->o1-o2);
Arrays.sort(intervals,(o1,o2)->o1[0]-o2[0]);
第一个会议先开始,那么他的结束时间加入最小堆
allocator.add(intervals[0][1]);
for (int i = 1; i < intervals.length; i++) {
最小堆的顶部是最先结束的会议时间
if (intervals[i][0] >= allocator.peek()) {
另一个会议的开始时间,大于等于最早的会议结束时间,那么这个会议开始
allocator.poll();
}
添加这个会议结束时间
allocator.add(intervals[i][1]);
}
return allocator.size();
}
}
class Solution {
public int minMeetingRooms(int[][] intervals) {
int[][] data=new int[intervals.length*2][2];
for (int i = 0; i < intervals.length; i++) {
将数据分解,时间+操作(开始+1,结束-1)
data[i*2][0]=intervals[i][0];
data[i*2][1]=1;
data[i*2+1][0]=intervals[i][1];
data[i*2+1][1]=-1;
}
排序按照时间先后,其次,对于结束和开始在同一时刻的,结束在前,开始在后,累计时则不会计入房间数
Arrays.sort(data,((o1, o2) -> {
if(o1[0]>o2[0]){
return 1;
}else if(o1[0]<o2[0]){
return -1;
}else{
return o1[1]-o2[1];
}
}));
int ans=0,cur=0;
for (int i = 0; i < data.length; i++) {
// System.out.println(data[i][0]+" "+data[i][1]);
cur+=data[i][1];
ans=Math.max(ans,cur);
}
return ans;
}
}
优化一下代码
class Solution {
public int minMeetingRooms(int[][] intervals) {
int n=intervals.length;
int[] start=new int[n];
int[] end = new int[n];
for (int i = 0; i < n; i++) {
start[i]=intervals[i][0];
end[i]=intervals[i][1];
}
Arrays.sort(start);
Arrays.sort(end);
int i=0,j=0,count=0,res=0;
while(i<n) {
if (start[i]<end[j]) {
count++;
i++;
res=Math.max(count, res);
}else if (start[i]>end[j]) {
count--;
j++;
}else {
i++;
j++;
}
}
return res;
}
}
我们都知道安卓有个手势解锁的界面,是一个 3 x 3 的点所绘制出来的网格。
给你两个整数,分别为 m 和 n,其中 1 ≤ m ≤ n ≤ 9,那么请你统计一下有多少种解锁手势,是至少需要经过 m 个点,但是最多经过不超过 n 个点的。
先来了解下什么是一个有效的安卓解锁手势:
每一个解锁手势必须至少经过 m 个点、最多经过 n 个点。
解锁手势里不能设置经过重复的点。
假如手势中有两个点是顺序经过的,那么这两个点的手势轨迹之间是绝对不能跨过任何未被经过的点。
经过点的顺序不同则表示为不同的解锁手势。
解释:
| 1 | 2 | 3 |
| 4 | 5 | 6 |
| 7 | 8 | 9 |
无效手势:4 - 1 - 3 - 6
连接点 1 和点 3 时经过了未被连接过的 2 号点。
无效手势:4 - 1 - 9 - 2
连接点 1 和点 9 时经过了未被连接过的 5 号点。
有效手势:2 - 4 - 1 - 3 - 6
连接点 1 和点 3 是有效的,因为虽然它经过了点 2 ,但是点 2 在该手势中之前已经被连过了。
有效手势:6 - 5 - 4 - 1 - 9 - 2
连接点 1 和点 9 是有效的,因为虽然它经过了按键 5 ,但是点 5 在该手势中之前已经被连过了。
示例:
输入: m = 1,n = 1
输出: 9
from functools import lru_cache
class Solution:
def numberOfPatterns(self, m: int, n: int) -> int:
不可达的map
graph = {
1: {3: 2, 7: 4, 9: 5},
2: {8: 5},
3: {1: 2, 7: 5, 9: 6},
4: {6: 5},
5: {},
6: {4: 5},
7: {1: 4, 3: 5, 9: 8},
8: {2: 5},
9: {1: 5, 3: 6, 7: 8},
}
ans = 0
@lru_cache(None)
def dfs(status, current, count):
if count == n:
return 1
current_ans = 0 if count < m else 1
for i in range(1, 10):
if status & (1 << i) == 0:
if i not in graph[current] or ((1 << graph[current][i]) & status):
current_ans += dfs(status | (1 << i), i, count + 1)
return current_ans
# for cur in range(1, 10):
# ans += dfs(1 << cur, cur, 1)
ans += 4 * dfs(1 << 1, 1, 1)
ans += 4 * dfs(1 << 2, 2, 1)
ans += dfs(1 << 5, 5, 1)
return ans