p485-最大连续1的个数
class Solution {
public int findMaxConsecutiveOnes(int[] nums) { //输入数组
int maxCount = 0, count = 0;
int n = nums.length; //得到数组长度
for (int i = 0; i < n; i++) {
if (nums[i] == 1) {
count++;
} else {
maxCount = Math.max(maxCount, count); //返回两个值中的最大值,存入maxCount中
count = 0; //遇到不是1的,count就会清零
}
}
maxCount = Math.max(maxCount, count); //比较
return maxCount;
}
}
561-数组拆分Ⅰ
//把排序的过程在脑中完成,直接编写求结果的程序
public static int arrayPairSum(int[] nums) {
Arrays.sort(nums); //排序
int sum = 0;
for (int i = 0; i < nums.length; i += 2) {
sum += nums[i]; //选第1、3、5、7、9个,
} //min的过程在脑子里完成,排序之后:(1,2),(3,4),min一下的话,就是求1+3+...
return sum;
}
566-重塑矩阵
方法1:技巧
public static int[][] matrixReshape(int[][] nums, int r, int c) {
int row = nums.length; //原来的行
int col = nums[0].length; //原来的列
if (row * col != r * c) {
return nums;
}
int[][] ans = new int[r][c]; //新的二维数组
int n = row * col;
for (int i = 0; i < n; i++) {
ans[i / c][i % c] = nums[i / col][i % col];
}
return ans;
}
方法2:for循环
public static int[][] matrixReshape(int[][] nums, int r, int c) {
int[][] arrtrans = new int[r][c];
int rr = nums.length;
int cc = nums[0].length;
int m = 0;
int n = 0;
if (r * c == rr * cc) {
for (int i = 0; i < r; i++) {
for (int j = 0; j < c; j++) {
arrtrans[i][j] = nums[m][n];
n++;
if (n == cc) { //粗暴循环方法,关键在这里
n = 0;
m++;
}
}
}
return arrtrans;
} else {
return nums;
}
}
001-两数之和
与其判断两个数怎么相加=目标值,不如找数组里有没有【目标值-数组中的数】,相当于先找出答案,再找答案所在的地方
方法1、暴力解法
//暴力解法
public static int[] twoSum(int[] nums, int target) {
int[] list = {0, 0};
for (int i = 0; i < nums.length-1; i++) {
for (int j = i+1; j < nums.length; j++) {
if (nums[i] + nums[j] == target) {
list[0] = i;
list[1] = j;
}
}
}
return list;
}
方法2、哈希表
//哈希表解法
public static int[] twoSum(int[] nums, int target) {
Map<Integer, Integer> hm = new HashMap<>();
int[] ints = {-1, -1};
for (int i = 0; i < nums.length; i++) {
if (hm.containsKey(target - nums[i])) {
ints[0] = hm.get(target - nums[i]);
ints[1] = i;
}
hm.put(nums[i], i);//把数组一个一个放进去,再一次for循环,这个时候i++了,就不会重复了。左k右v。
//为什么把hm.put放到后面?防止出现第一个值就符合条件的情况,这样的话就会返回【0,0】
//如{1,2,3,5},依次放入hm的是<1,0>、<2,1>、<3,2>、<5,3>,如果符合条件的话就会将哈希表中的键值赋值给ints数组
}
return ints; //返回ints数组
}
020-有效的括号
方法一、栈
public boolean isValid(String s) {
if (s.isEmpty())
return true;
Stack<Character> stack = new Stack<>();
//栈的用法
for (char k : s.toCharArray()) {//取每一个字符
if (k == '(')
stack.push(')');//压栈,压入对应的另一半
else if (k == '{')
stack.push('}');
else if (k == '[')
stack.push(']');
else if (k == ')' || k == '}' || k == ']') {
if (stack.empty()) {
return false;
}
Character pop = stack.pop();
if (k!=pop){
return false;
}
}
}
return stack.empty();
}
//重点是学习栈怎么使用
//栈是FILO模式
stack<Character> stack = new Stack<>();
//创建空栈
stack.push('}');
//压栈,可以压入各种类型
stack.pop();
//弹栈,可以加返回值
方法二、暴力解法
class Solution {
public boolean isValid(String s) {
while (s.contains("()") || s.contains("[]") || s.contains("{}")) {
if (s.contains("()")) {
s = s.replace("()", "");
}
if (s.contains("[]")) {
s = s.replace("[]", "");
}
if ((s.contains("{}"))) {
s = s.replace("{}", "");
}
}
return s.length() == 0;
}
}
101-对称二叉树
//方法传入的结点直接进行比较,然后末尾再传入结点的子节点,即可完成递归操作
//递归法,迭代法还不懂
public boolean isSymmetric(TreeNode root) {
return check(root, root);//关键:传入两个root
}
public boolean check(TreeNode p, TreeNode q) {
if (p == null && q == null) {
return true;
}
if (p == null || q == null) {
return false;
}
return p.val == q.val && check(p.left, q.right) && check(p.right, q.left);
}
剑指OFFER
029-顺时针打印矩阵
//重点:观察四条边的个数是如何变化的
//我的思路是先转置一下,读的时候读一行原矩阵,再读一行转置矩阵,还是有点麻烦,不需要转置
public int[] spiralOrder(int[][] matrix) {
if (matrix == null || matrix.length == 0 || matrix[0].length == 0)
return new int[0];
int[] ans = new int[matrix.length * matrix[0].length];//一维数组的声明
helper(matrix, 0, 0, matrix[0].length, matrix.length, ans);
return ans;
}
public void helper(int[][] matrix, int x, int y, int width, int height, int[] ans) {
int count = 0;
while (width > 0 && height > 0) { //每次转完一圈之后判断一下
for (int i = 0; i < width; i++) //读行
ans[count++] = matrix[y][x + i];
for (int i = 1; i < height; i++) //读右边列
ans[count++] = matrix[y + i][x + width - 1];
if (height > 1) { //高度大于1,转完一圈高度值才会减小。如果是=1的话,会重复读
for (int i = width - 2; i >= 0; i--) //逆序读下面一行
ans[count++] = matrix[y + height - 1][x + i];
}
if (width > 1) { //宽度大于1
for (int i = height - 2; i > 0; i--) //逆序读左面一列
ans[count++] = matrix[y + i][x];
}
width -= 2;//每转完一圈,宽度-2
height -= 2;//每转完一圈,高度-2
x += 1;
y += 1;
}
}
136-只出现一次的数字
//方法一、new一个ArrayList集合,利用ArrayList的contains方法判断集合中是否存在该元素,利用remove方法可以删除元素或者指定索引处的元素
public int singleNumber(int[] nums) {
List<Integer> list = new ArrayList<>();
for (int i = 0; i < nums.length; i++) {
if (!list.contains(nums[i])) {
list.add(nums[i]);//不包含则添加
} else {
Integer num = nums[i];//如果用int类型的话,会让系统以为是索引,强制转成Integer类型,只能是元素了
list.remove(num);//包含则删除
}
}
return list.get(0);
}
方法二、位运算:异或
原理是:相异才为1,相同则为0
public static int singleNumber(int[] nums) {
int ans = 0;
for (int num : nums) {
ans ^= num;//连续的两个数字自动转化成二进制后进行异或运算
//如果第三个数字和之前的数字相同,再进行异或运算的时候会自动把该数字代表的值转为0
}
return ans;
}
剑指OFFER
面试题10-2-青蛙跳台阶
转化成了斐波那契问题,可以建立斐波那契数列,然后检索即可,
也可以使用动态规划方法,如下:
public int numWays(int n) {
int a = 1, b = 1, sum;//a代表跳0阶有多少方法,b代表跳1阶有多少方法
for (int i = 0; i < n; i++) {
sum = (a + b) % 1000000007;
//sum代表跳下一阶有多少方法,有该阶的前两阶的方法之和(见图)
//取模是为了防止内存越界
a = b;//a,b,sum,滚动前进,sum先更新(算出这次有多少方法),然后a更新,然后b更新
b = sum;//辅助变量 sum 使 a, b两数字交替前进
}
return a;
}
public static ListNode reverseList(ListNode head) {
ListNode prev = null;
ListNode curr = head;
while (curr != null) {//当前不为空的话
ListNode next = curr.next;//记住下一个结点
curr.next = prev;//链表的next指向前一个结点,实现反转
prev = curr;//prev往后走
curr = next;//curr往后走
}
return prev; //返回的是这个
}
234-回文链表
确定数组列表是否回文很简单,我们可以使用双指针法来比较两端的元素,并向中间移动。一个指针从起点向中间移动,另一个指针从终点向中间移动。因此最简单的方法就是将链表的值复制到数组列表中,再使用双指针法判断。(摘自力扣)
如果要使用反转的方法,需要一半一半进行反转,不能反转整个链表
public boolean isPalindrome(ListNode head) {
List<Integer> vals = new ArrayList<Integer>();
// 将链表的值复制到数组中
ListNode curr = head;
while (curr != null) {
vals.add(curr.val);
curr = curr.next;
}
// 使用双指针判断是否回文
int front = 0;
int back = vals.size() - 1;
while (front < back) {
if (!vals.get(front).equals(vals.get(back))) {
return false;
}
front++;
back--;
}
return true;
}
剑指OFFER
22-链表中倒数第k个节点
//快慢指针
//必须学会快慢指针的用法
public ListNode getKthFromEnd(ListNode head, int k) {
ListNode former = head, latter = head;
for (int i = 0; i < k; i++) {
former = former.next;//快指针先走k步
}
while (former != null) {//跳出条件:快指针走完
former = former.next;
latter = latter.next;
}
return latter;
}
283-移动零
public void moveZeroes(int[] nums) {
int index = 0;
for (int num : nums) {
if (num != 0) {
nums[index++] = num;//不是0的话,把数组的第1个变成这个数
}
}//结束后index就变成了最后一个非0元素的索引
for (int i = index; i < nums.length; i++) {
nums[i] = 0;//从index后一个元素到数组结束,全置为0
}//完成移动
}
155-最小栈
//因为要保证,栈顶元素pop出后,上一个最小值还在,所以使用栈结构,FILO
public class MinStack {
private Stack<Integer> dataStack;
private Stack<Integer> minStack;//辅助栈
public MinStack() {
dataStack = new Stack<>();
minStack = new Stack<>();
}
public void push(int x) {
dataStack.push(x);
// 注意:这里必须是 x <= minStack.peek() 的时候,就 push minStack
// 如果去掉等于的话,可能会出现 dataStack 不为空,但是 minStack 为空了
if (minStack.isEmpty() || x <= minStack.peek()) {//peak是栈顶
minStack.push(x);
}
}
public void pop() {
int topElement = dataStack.pop();
if (topElement == minStack.peek()) {
minStack.pop();
}
}
public int top() {
return dataStack.peek();
}
public int getMin() {
return minStack.peek();
}
}
public int maxSubArray(int[] nums) {
int res = nums[0];//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;
}
21-合并两个有序链表
public ListNode mergeTwoLists(ListNode l1, ListNode l2) {
ListNode prehead = new ListNode(-1);
ListNode prev = prehead;
while (l1 != null && l2 != null) {
if (l1.val <= l2.val) {
prev.next = l1;//直接=那个结点就行,=那个结点的值是不对的
l1 = l1.next;//l1后移
} else {
prev.next = l2;
l2 = l2.next;
}
prev = prev.next;//prev后移
}
// 合并后 l1 和 l2 最多只有一个还未被合并完,我们直接将链表末尾指向未合并完的链表即可
prev.next = l1 == null ? l2 : l1;
return prehead.next;
}
剑指21-调整数组顺序使奇数在前偶数在后
方法一:简单粗暴,申请一个临时数组
public int[] exchange(int[] nums) {
if (nums == null || nums.length == 0)
return nums;
int[] ints = new int[nums.length];//设置的时候,带上占用的空间
int j = 0;
int length = nums.length - 1;
for (int i = 0; i < nums.length; i++) {
if (nums[i] % 2 != 0) {
ints[j++] = nums[i];
} else {
ints[length--] = nums[i];
}
}
return ints;
}
public int[] exchange(int[] nums) {
int left = 0;
int right = nums.length - 1;
while (left < right) {
// 当找到一个偶数时,就跳出循环。
// (这里有个求奇偶数的小技巧,就是当一个数是奇数时,它的二进制表示的最后一位肯定是1
while (left < right && (nums[left] & 1) == 1) {
left++;
}
// 当找到一个奇数时,就跳出循环
while (left < right && (nums[right] & 1) == 0) {
right--;
}
// 如果两个指针还没有碰到一起时,说明找到了需要交换的位置
if (left < right) {
int temp = nums[left];
nums[left] = nums[right];
nums[right] = temp;
}
}
return nums;
}
public int maxSubArray(int[] nums) {
int pre = 0;//前i项的最大值
int maxAns = nums[0];
for (int x : nums) {
pre = Math.max(pre + x, x);//f(i-1)+nums[i]与nums[i]比较
maxAns = Math.max(maxAns, pre);//全部数组中的最大连续子序和,有更大的值就更新
}
return maxAns;
}
104-二叉树的最大深度
public int maxDepth(TreeNode root) {
if (root == null) {
return 0;
} else {
int leftHeight = maxDepth(root.left);
int rightHeight = maxDepth(root.right);
return Math.max(leftHeight, rightHeight) + 1; //包含根节点
}
}
剑指58-Ⅰ
public String reverseWords(String s) {
String[] strs = s.trim().split(" "); // 删除首尾空格,分割字符串
StringBuilder res = new StringBuilder();
for (int i = strs.length - 1; i >= 0; i--) { // 倒序遍历单词列表
if (strs[i].equals("")) continue; // 遇到空单词则跳过
res.append(strs[i] + " "); // 将单词拼接至 StringBuilder
}
return res.toString().trim(); // 转化为字符串,删除尾部空格,并返回
}
169-多数元素
Boyer-Moore 投票算法
如果候选人不是maj 则 maj,会和其他非候选人一起反对 会反对候选人,所以候选人一定会下台(maj==0时发生换届选举)
如果候选人是maj , 则maj 会支持自己,其他候选人会反对,同样因为maj 票数超过一半,所以maj 一定会成功当选
public int majorityElement(int[] nums) {
int count = 0;
Integer candidate = null;
for (int num : nums) {
if (count == 0) {
candidate = num;
}
count += (num == candidate) ? 1 : -1;
}
return candidate;
}
剑指52-Ⅱ
0~n-1中缺失的数字
二分法
public int missingNumber(int[] nums) {
int i = 0, j = nums.length - 1;//数组头尾
while(i <= j) {
int m = (i + j) / 2;//取数组中间
if(nums[m] == m) i = m + 1;//如果符合要求,说明目标在后半部分
//就把头变成[旧的中间+1],下一次循环的时候,[新的中间]就变成了[旧的中间+1]和[尾]的中间的值
else j = m - 1;//不符合的话,说明在前半部分,调整尾的指向
}
return i;
}
public int add(int a, int b) {
int sum = a ^ b;
int carry = (a & b) << 1; //注意括号
while (carry != 0) {
a = sum;
b = carry;
sum = a ^ b;
carry = (a & b) << 1; //注意括号
}
return sum;
}
461-汉明距离
方法一、使用内置函数
public int hammingDistance(int x, int y) {
return Integer.bitCount(x ^ y); +
//bitCount 数出整数二进制下 1 的个数
//1^0 = 1 ,0^1 =1 ,0^0 = 0 ,1^1 = 0
}
方法二、异或运算
异或运算:相同的为0,不同的为1
将两个数做异或运算,然后计算1的个数即是答案。
将x,y按位异或得到i,将问题转化为求i的二进制位中1的个数count
当i不为0时,将i与1按位与,判断二进制末尾是不是1,是,count++
将i右位移一位
重复第二,第三步,直到第二步条件不满足,,即i==0时终止统计, 即可得到i的二进制位中1的个数,问题得解。
public int hammingDistance(int x, int y) {
int xor = x ^ y; //求异或,不同的位=1
int distance = 0; //初始距离为0
while (xor != 0) { //异或后的结果的每一位都运算完毕后,跳出循环
if (xor % 2 == 1) //对2取余,相当于看最后一位是不是1
distance += 1; //最后一位是1的话,距离+1
xor = xor >> 1; //一开始异或后的结果往后移一位
}
return distance;
}
70-爬楼梯
动态规划:滚动数组
for循环求出斐波那契数列,一步一步往后滚动
迭代法不行,会超出时间限制
public int climbStairs(int n) {
int p = 0, q = 0, r = 1;
for (int i = 1; i <= n; ++i) { //是++i
p = q;
q = r;
r = p + q;
}
return r;
}
剑指
面试题55-1
方法一、DFS
KEY:此树的深度和其左(右)子树的深度之间的关系。
显然,此树的深度等于左子树的深度 与 右子树的深度中的最大值 +1 。
public int maxDepth(TreeNode root) {
//DFS,后序遍历,递归实现
if(root == null) return 0;
return Math.max(maxDepth(root.left), maxDepth(root.right)) + 1;
}
方法二、BFS
层序遍历,就是要把每一层的子节点一个一个排好队
public int maxDepth(TreeNode root) {
if (root == null)
return 0;
//创建一个队列
Deque<TreeNode> deque = new LinkedList<>();
//树的根节点压入链表
deque.push(root);
//深度初始化为0
int count = 0;
//链表非空的话,就循环
while (!deque.isEmpty()) {
//size方法取出每一层结点的个数
int size = deque.size();
//该层的个数>0就循环
while (size > 0) {
//弹出链表中的结点,并置为当前结点
TreeNode cur = deque.pop();
//把当前结点的左右子结点放入链表(非空的话)【层序遍历就是这么来的】
if (cur.left != null)
deque.addLast(cur.left); //添加到尾部
if (cur.right != null)
deque.addLast(cur.right);
//执行完该层的一个结点后,size--
size--;
}
//内循环完成一次执行后,就把该层的所有结点都弹出了,并把该层所有子节点加入链表(非空的话)
//所以深度++,准备下一次的循环
count++;
}
return count;
}
705-设计哈希集合
class MyHashSet {
//定义哈希表的大小=769,一个质数,有效解决哈希冲突
private static final int BASE = 769;
//定义链表
private LinkedList[] data;
//构造方法:创建了哈希集合,集合的元素都是链表
public MyHashSet() {
//创建链表,大小=BASE
data = new LinkedList[BASE];
for (int i = 0; i < BASE; ++i) {
//数组里面的每个元素,都new一个链表
data[i] = new LinkedList<Integer>();
}
}
//add方法
public void add(int key) {
//找到key对应的h处
int h = hash(key);
//使用h处链表的迭代器,用于遍历
Iterator<Integer> iterator = data[h].iterator();
//h处有值的话,就一直找链表的下一个值
while (iterator.hasNext()) {
Integer element = iterator.next();
//直到找到下一个元素=key,退出while循环
if (element == key) {
return; //这里不一样
}
}
//h处链表的末尾增加一个key
data[h].offerLast(key);
}
//remove方法
public void remove(int key) {
//找到key对应的h处
int h = hash(key);
Iterator<Integer> iterator = data[h].iterator();
while (iterator.hasNext()) {
Integer element = iterator.next();
//直到找到下一个元素=key,删除该处的key,退出while循环
if (element == key) {
data[h].remove(element); //这里不一样
return;
}
}
}
//存在就返回true
public boolean contains(int key) {
int h = hash(key);
Iterator<Integer> iterator = data[h].iterator();
while (iterator.hasNext()) {
Integer element = iterator.next();
if (element == key) {
return true; //这里不一样
}
}
return false;
}
//判断新的值放到哪里
private static int hash(int key) {
//对BASE取余
return key % BASE;
}
}
121-买卖股票的最佳时机
public int maxProfit(int[] prices) {
int minprice = Integer.MAX_VALUE;//Integer的最大值
int maxprofit = 0;//暂时=0
for (int i = 0; i < prices.length; i++) {
if (prices[i] < minprice) {//如果某一项数字是最小的(目前最小的,以后循环可能更小)
minprice = prices[i];//找到目前循环数组里面的最小值
} else if (prices[i] - minprice > maxprofit) {
//某一项大于目前循环的最小值,如果大于maxprofit
maxprofit = prices[i] - minprice;//更改maxprofit
}
}
return maxprofit;
}
剑指50
第一个只出现一次的字符
public char firstUniqChar(String s) {
HashMap<Character, Boolean> map = new HashMap<>();//创建HashMap
char[] sc = s.toCharArray();//字符串转换为字符串数组
for(char c : sc)//循环字符串数组
map.put(c, !map.containsKey(c));//如果有值了,就把对应的项改为false
for(char c : sc)//循环字符串数组
if(map.get(c)) return c;//循环,返回第一个true对应的字母
return ' ';//没有找到的话,返回空串
}