剑指 Offer 03. 数组中重复的数字
如果没有重复数字,那么正常排序后,数字i应该在下标为i的位置,所以思路是重头扫描数组,遇到下标为i的数字如果不是i的话,(假设为m),那么我们就拿与下标m的数字交换。在交换过程中,如果有重复的数字发生,那么终止返回ture
class Solution {
public int findRepeatNumber(int[] nums) {
int len = nums.length;
for(int i = 0; i < len;i++) {
while(nums[i]!=i) {
if(nums[nums[i]]==nums[i])
return nums[i];
else // 交换nums[i] 与 nums[nums[i]]
swap(nums,nums[i],i);
}
}
return -1;
}
void swap(int[] nums,int a,int b){
int t = nums[a];
nums[a] = nums[b];
nums[b] = t;
}
}
51. N 皇后
class Solution {
List<List<String>> res = new ArrayList();
List<String> list = new ArrayList();
public List<List<String>> solveNQueens(int n) {
int[] len = new int[n];
dfs(len,0);
return res;
}
void dfs(int[] arr,int start){
// 找到位置,转变成字符串
if(start==arr.length){
for(int c:arr) {
char[] ch = new char[arr.length];
Arrays.fill(ch,'.');
int i = 0;
while((c&(1<<i))==0) {
i++;
}
ch[ch.length-i-1]='Q';
list.add(new String(ch));
}
res.add(new ArrayList(list));
list.clear();
return;
}
// 当前行的棋盘分布
int now = arr[start];
// 依次实验该行所有的位置放置queen
while(now<=(1<<(arr.length-1))){
if(now==0) {
now=1;
arr[start] = 1;
} else {
arr[start] = now;
}
// 判断
if(check(arr,start)) {
dfs(arr,start+1);
}
now=now<<1;
}
// 一定不要忘了归位,比如[1,4,0,0]时候,第三行无解,此时[1,4,8,0],如果第三行不归零,下一次now直接变成了8,而不是0
arr[start] = 0;
}
boolean check(int[] arr,int start) {
// 检查列
for(int i = start-1;i>=0;i--) {
if((arr[start]&arr[i])!=0)
return false;
}
// \斜对角线
int now = arr[start]<<1;
for(int i = start-1;i>=0;i--,now<<=1) {
if((now&arr[i])!=0)
return false;
}
// /斜对角线
now = arr[start]>>1;
for(int i = start-1;i>=0;i--,now>>=1) {
if((now&arr[i])!=0)
return false;
}
return true;
}
}
52. N皇后 II
class Solution {
int res = 0;
public int totalNQueens(int n) {
int[] len = new int[n];
dfs(len,0);
return res;
}
void dfs(int[] arr,int start){
if(start==arr.length){
res++;
return;
}
int now = arr[start];
while(now<=(1<<(arr.length-1))){
if(now==0) {
now=1;
arr[start] = 1;
} else {
arr[start] = now;
}
// 判断
if(check(arr,start)) {
dfs(arr,start+1);
}
now=now<<1;
}
// 一定不要忘了归位,比如[1,4,0,0]时候,第三行无解,此时[1,4,8,0],如果第三行不归零,下一次now直接变成了8,而不是0
arr[start] = 0;
}
boolean check(int[] arr,int start) {
// 检查列
for(int i = start-1;i>=0;i--) {
if((arr[start]&arr[i])!=0)
return false;
}
int now = arr[start]<<1;
for(int i = start-1;i>=0;i--,now<<=1) {
if((now&arr[i])!=0)
return false;
}
now = arr[start]>>1;
for(int i = start-1;i>=0;i--,now>>=1) {
if((now&arr[i])!=0)
return false;
}
return true;
}
}
2. 两数相加
class Solution {
public ListNode addTwoNumbers(ListNode l1, ListNode l2) {
ListNode dummy = new ListNode();
ListNode pre = dummy;
boolean f = false; // 进位
while(l1!=null || l2!=null || f){
int v1 = l1==null?0:l1.val; // 这样不必单独判断某个链表是不是空了
int v2 = l2==null?0:l2.val;
int v = v1+v2;
if(f){
v++;
}
if(v>=10){
v-=10;
f=true;
} else {
f = false;
}
pre.next = new ListNode(v);
pre = pre.next;
if(l1!=null){
l1=l1.next;
}
if(l2!=null){
l2=l2.next;
}
}
return dummy.next;
}
}
445. 两数相加 II
用栈
- 然后依次出栈
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode(int x) { val = x; }
* }
*/
class Solution {
public ListNode addTwoNumbers(ListNode h1, ListNode h2) {
Stack<ListNode> s1 = new Stack();
Stack<ListNode> s2 = new Stack();
ListNode l1 = h1;
ListNode l2 = h2;
while(l1!=null) {
s1.push(l1);
l1=l1.next;
}
while(l2!=null){
s2.push(l2);
l2 = l2.next;
}
ListNode dummy = new ListNode();
boolean f = false;
while(!s1.isEmpty() || !s2.isEmpty() || f) {
int v1 = s1.isEmpty()?0:s1.pop().val;
int v2 = s2.isEmpty()?0:s2.pop().val;
int v = v1+v2;
if(f){
v+=1;
}
if(v>=10) {
v-=10;
f = true;
} else {
f = false;
}
ListNode p = new ListNode(v);
p.next = dummy.next;
dummy.next = p;
}
return dummy.next;
}
}
55. 跳跃游戏
- 然后依次出栈
遍历数组,判断当前节点可以到达的最远距离,更新最远距离
class Solution {
public boolean canJump(int[] nums) {
if(nums.length==1)
return true;
int last = nums[0]; // 当前可以到达的最远距离
if(last>=nums.length-1)
return true;
for(int i = 1; i < nums.length;i++) {
if(last<i) { // 当前距离不可达
return false;
}
last = Math.max(i+nums[i],last);
if(last>=nums.length-1)
return true;
}
return false;
}
}
45. 跳跃游戏 II
我们维护当前能够到达的最大下标位置,记为边界。我们从左到右遍历数组,到达边界时,更新边界并将跳跃次数增加 1。class Solution {
public int jump(int[] nums) {
int end = 0; // 上一次跳跃最后可以达到的位置
int maxPos = 0; // 已经探索到的最大的位置
int len = nums.length;
if(len==1)
return 0;
int step = 0;
for(int i = 0 ; i < len;i++) {
maxPos = Math.max(maxPos,i+nums[i]);
if(maxPos>=len-1) { // 从当前节点跳跃可以跳到最后位置及其以后的位置
return step+1;
}
if(i==end) { // 已经到达了上一次探索的极限,需要加一步了
step++;
end = maxPos; // 这一步可以探索到的极限
}
}
return step;
}
}
128. 最长连续序列
去重
遍历每个元素,找每个开头
class Solution {
public int longestConsecutive(int[] nums) {
Set<Integer> set = new HashSet();
for(int i:nums){ // 去重
set.add(i);
}
int longest = 0;
for(int now:set) {
// 如果没有比当前数字小一的数字,那它就是开头的数字
if(!set.contains(now-1)) {
int nowLong = 1;
// 一次找比他大的数字
while(set.contains(now+1)) {
now++;
nowLong++;
}
longest = Math.max(longest,nowLong);
}
}
return longest;
}
}
347. 前 K 个高频元素
- 用hash记录每个元素出现的次数
- 使用一个k大小的小顶堆
遍历hash,如果大于堆顶,弹出,加入当前元素
class Solution {
public int[] topKFrequent(int[] nums, int k) {
Map<Integer,Integer> map = new HashMap();
for(int num:nums){
map.put(num,map.getOrDefault(num,0)+1);
}
PriorityQueue<int[]> set = new PriorityQueue(new Comparator<int[]>(){
public int compare(int[] m,int[] n) {
return m[1]-n[1];
}
});
for (Map.Entry<Integer, Integer> entry : map.entrySet()) {
if(set.size()<k) {
set.add(new int[]{entry.getKey(),entry.getValue()});
} else {
if(set.peek()[1]<entry.getValue()) {
set.poll();
set.add(new int[]{entry.getKey(),entry.getValue()});
}
}
}
int[] ret = new int[k];
for (int i = 0; i < k; ++i) {
ret[i] = set.poll()[0];
}
return ret;
}
}