一.字符串处理
1. 空格替换 力扣
class Solution {
// 简单方法:replaceAll()
public String replaceSpace1(String s) {
if(s == null || s.length() == 0){
return s;
}
return s.replaceAll(" ", "%20");
}
// 常规方法:StringBuilder
public String replaceSpace2(String s) {
if(s == null || s.length() == 0){
return s;
}
StringBuilder sb = new StringBuilder();
for(int i = 0; i<s.length(); i++){
if(s.charAt(i) == ' '){
sb.append("%20");
}else{
sb.append(s.charAt(i));
}
}
return sb.toString();
}
}
2.左旋转字符串 力扣
class Solution {
// 简单方法,额外内存
public String reverseLeftWords(String s, int n) {
if(s == null || s.length() <= n){
return s;
}
String s1 = s.substring(0, n);
String s2 = s.substring(n, s.length());
return s2+s1;
}
// 不使用额外内存方法 按n切开,各自翻转字符串,最后整体翻转一次
public String reverseLeftWords(String s, int n) {
if (s == null || s.length() == 0) {
return s;
}
char[] ss = s.toCharArray();
reserve(ss, 0, n-1);
reserve(ss, n, ss.length-1);
reserve(ss, 0, ss.length-1);
return new String(ss);
}
public void reserve(char[] s, int left, int right) {
while (left <= right) {
char temp = s[left];
s[left] = s[right];
s[right] = temp;
left++;
right--;
}
}
}
3.表示数值的字符串 力扣
通过有限状态机处理
class Solution {
public boolean isNumber(String s) {
if(s == null || s.length() == 0){
return false;
}
Map[] stage = {
new HashMap(){{put('k', 0); put('d',2); put('s', 1); put('t',3);}}, // 0
new HashMap(){{put('d', 2); put('t',3);}}, // 1
new HashMap(){{put('d', 2);put('t',9);put('e',5);put('k', 8);}}, // 2
new HashMap(){{put('d', 4);}}, // 3
new HashMap(){{put('d', 4);put('e',5);put('k', 8);}}, // 4
new HashMap(){{put('s', 6);put('d',7);}}, // 5
new HashMap(){{put('d', 7);}}, // 6
new HashMap(){{put('d', 7); put('k', 8);}}, // 7
new HashMap(){{put('k', 8);}}, // 8
new HashMap(){{put('k', 8);put('d', 4);put('e', 5);}} // 9
};
int p = 0;
for(int i=0; i<s.length(); i++){
if(!stage[p].containsKey(getChar(s.charAt(i)))){
return false;
}
p = (Integer)stage[p].get(getChar(s.charAt(i)));
}
if(p == 2||p==4||p==7||p==8||p==9){
return true;
}
return false;
}
private Character getChar(char c) {
if(c>='0'&&c<='9'){
return 'd';
}
if(c == '.'){
return 't';
}
if(c == ' '){
return 'k';
}
if(c == 'e' || c == 'E'){
return 'e';
}
if(c == '+' || c == '-'){
return 's';
}
return null;
}
}
4. 把字符串转换成整数力扣
Java 中的整数范围
整数 | 值 |
---|---|
最小值 | -2147483648 |
最大值 | 2147483647 |
public int strToInt(String str) {
if (str == null || str.length() == 0) {
return 0;
}
int i = 0;
int count = 0;
int flag = 1;
while (i < str.length() && str.charAt(i) == ' ') {
i++;
}
if (i < str.length() && (str.charAt(i) == '+' || str.charAt(i) == '-')) {
flag = str.charAt(i) == '+' ? 1 : -1;
i++;
}
while (i < str.length()) {
int tmp = str.charAt(i) - '0';
if (tmp < 0 || tmp > 9) {
return count*flag;
}
if (count < Integer.MAX_VALUE / 10 || (count == Integer.MAX_VALUE / 10 && tmp <= 7)) {
count = count * 10 + tmp;
}else{
return flag>0?Integer.MAX_VALUE:Integer.MIN_VALUE;
}
i++;
}
return count*flag;
}
反转单词顺序 力扣
// split分割
public String reverseWords(String s) {
if(s==null ||s.length() == 0){
return s;
}
String[] words = s.split(" ");
StringBuffer sb = new StringBuffer();
for(int i = words.length-1; i>=0; i--){
if(!words[i].equals("")){
sb.append(words[i]);
sb.append(" ");
}
}
String result = sb.toString();
// 注意判断长度
return result.length()>0?result.substring(0, result.length()-1):"";
}
// 双指针反转,不去除空格,每个单词反转,再整体反转
public String reverseWords(String s) {
if (s == null || s.length() == 0) {
return s;
}
char[] chars = s.toCharArray();
int i = 0;
int j = 0;
while (i < chars.length) {
while (i < chars.length && chars[i] == ' ') {
i++;
j++;
}
while (j < chars.length && chars[j] != ' ') {
j++;
}
swap(chars, i, j-1);
i=j;
}
swap(chars, 0, chars.length-1);
return String.valueOf(chars);
}
第一个只出现一次的字符(LinkedHashMap/普通hash遍历两次)
// 普通哈希
class Solution {
public char firstUniqChar(String s) {
HashMap<Character, Boolean> dic = new HashMap<>();
char[] sc = s.toCharArray();
for(char c : sc)
dic.put(c, !dic.containsKey(c));
for(char c : sc)
if(dic.get(c)) return c;
return ' ';
}
}
// 有序哈希LinedHashMap
class Solution {
public char firstUniqChar(String s) {
Map<Character, Boolean> dic = new LinkedHashMap<>();
char[] sc = s.toCharArray();
for(char c : sc)
dic.put(c, !dic.containsKey(c));
for(Map.Entry<Character, Boolean> d : dic.entrySet()){
if(d.getValue()) return d.getKey();
}
return ' ';
}
}
剑指 Offer 38. 字符串的排列
Set<String> result = new HashSet<>();
public String[] permutation(String s) {
if (s == null || s.length() == 0) {
return new String[0];
}
permutation(s.toCharArray(), 0);
return (String[]) new ArrayList(result).toArray(new String[result.size()]);
}
// 每个位置的字符和后面的字符替换
public void permutation(char[] s, int index) {
if (index == s.length - 1) {
result.add(String.valueOf(s));
return;
}
// set用于剪枝
HashSet<Character> set = new HashSet<>();
for (int i = index; i < s.length; i++) {
if (!set.contains(chars[i])) {
set.add(chars[i]);
swap(s, i, index);
permutation(s, index + 1);
swap(s, i, index);
}
}
}
public void swap(char[] s, int i, int j) {
char tmp = s[i];
s[i] = s[j];
s[j] = tmp;
}
12. 整数转罗马数字
public String intToRoman(int num) {
// I 1
// IV 4
// IX 9
// X 10
// XL 40
// L 50
// XC 90
// C 100
// CD 400
// D 500
// CM 900
// M 1000
if(num<=0){
return "";
}
StringBuilder sb = new StringBuilder();
int[] nums = new int[]{1000,900,500,400,100,90,50,40,10,9,5,4,1};
String[] strs = new String[]{"M", "CM", "D", "CD", "C", "XC", "L", "XL", "X", "IX", "V", "IV", "I"};
for(int i =0; i<nums.length;i++){
while(num-nums[i]>=0){
num = num-nums[i];
sb.append(strs[i]);
}
if(num == 0){
break;
}
}
return sb.toString();
}
13. 罗马数字转整数
public int romanToInt(String s) {
if(s == null || s.length() == 0){
return 0;
}
Map<Character, Integer> str2num = new HashMap<>(){{
put('I', 1);
put('V', 5);
put('X', 10);
put('L', 50);
put('C', 100);
put('D', 500);
put('M', 1000);
}};
int result = 0;
// 前小于后,为负数,前大于后,为正数
for(int i = 0;i<s.length();i++){
if(i+1<s.length() && str2num.get(s.charAt(i))<str2num.get(s.charAt(i+1))){
result += -str2num.get(s.charAt(i));
}else{
result += str2num.get(s.charAt(i));
}
}
return result;
}
二、链表
1.反转链表:用到中间node "next"
public ListNode reverseList(ListNode head) {
if (head == null || head.next == null) {
return head;
}
ListNode pre = null;
ListNode cur = head;
while (cur != null) {
ListNode next = cur.next;
cur.next = pre;
pre = cur;
cur = next;
}
return pre;
}
2.复制复杂链表 剑指 Offer 35. 复杂链表的复制 - 力扣(LeetCode)
// 通过两个哈希表,内存占用多
public ListNode copyRandomList(ListNode head) {
if (head == null) {
return head;
}
Map<ListNode, ListNode> old2new = Maps.newHashMap();
Map<ListNode, ListNode> new2old = Maps.newHashMap();
ListNode newHead = genNewListNodeByOld(head, old2new, new2old);
ListNode cur = newHead;
while (head.next != null) {
cur.next = genNewListNodeByOld(head.next, old2new, new2old);
cur = cur.next;
head = head.next;
}
cur = newHead;
while (cur != null) {
cur.random = old2new.get(new2old.get(cur).random);
cur = cur.next;
}
return newHead;
}
public ListNode genNewListNodeByOld(ListNode oldNode, Map<ListNode, ListNode> old2New,
Map<ListNode, ListNode> new2Old) {
ListNode newNode = new ListNode(oldNode.val);
old2New.put(oldNode, newNode);
new2Old.put(newNode, oldNode);
return newNode;
}
// 通过链表重构加链表拆分
public ListNode copyRandomList(ListNode head) {
if (head == null) {
return head;
}
ListNode newHead = head;
while(head!=null){
ListNode newNode = new ListNode(head.val);
newNode.next = head.next;
head.next = newNode;
head = head.next.next;
}
head = newHead;
while(head!=null){
// 注意: 特别处理null!!!
head.next.random = head.random == null?null:head.random.next;
head = head.next.next;
}
head = newHead;
newHead = newHead.next;
while(head!=null){
ListNode newNode = head.next;
head.next = head.next.next;
// 注意: 特别处理null!!!
newNode.next = newNode.next == null?null:newNode.next.next;
head = head.next;
}
return newHead;
}
删除链表中的一个节点 剑指 Offer 18. 删除链表的节点 - 力扣(LeetCode)
public ListNode deleteNode(ListNode head, int val) {
ListNode begin = head;
ListNode preview = null;
while (begin != null) {
if (begin.val == val) {
if (preview != null) { // 注意判空
preview.next = begin.next;
} else {
head = head.next;
}
}
preview = begin;
begin = begin.next;
}
return head;
}
获取链表倒数第k个节点
// 通过arrayList获取
public ListNode getKthFromEnd(ListNode head, int k) {
if(head == null){
return null;
}
List<ListNode> nodes = new ArrayList<>();
while(head!=null){
nodes.add(head);
head = head.next;
}
if(nodes.size()<k){
return null;
}
return nodes.get(nodes.size()-k);
}
// 定义两个指针,第一个走了k个,第二个再走,第一个到终点时第二个到倒数k点
public ListNode getKthFromEnd(ListNode head, int k) {
if(head == null){
return head;
}
ListNode latter = head;
while(head!=null&&k>0){
head = head.next;
k--;
}
if(head == null&&k>0){
return null;
}
while(head!=null){
latter = latter.next;
head = head.next;
}
return latter;
}
合并排序链表
public ListNode mergeTwoLists(ListNode l1, ListNode l2) {
ListNode begin = new ListNode(0);
ListNode p = begin;
while(l1!=null && l2!=null){
if(l1.val<=l2.val){
begin.next = l1;
l1 = l1.next;
}else{
begin.next = l2;
l2 = l2.next;
}
begin = begin.next;
}
while(l1!=null){
begin.next = l1;
l1 = l1.next;
begin = begin.next;
}
while(l2!=null){
begin.next = l2;
l2 = l2.next;
begin = begin.next;
}
return p.next;
}
剑指 Offer 52. 两个链表的第一个公共节点
// 直观方法,两个stack
ListNode getIntersectionNode(ListNode headA, ListNode headB) {
Stack<ListNode> a = new Stack<>();
Stack<ListNode> b = new Stack<>();
for(ListNode head = headA; head !=null; head = head.next){
a.push(head);
}
for(ListNode head = headB; head!=null; head = head.next){
b.push(head);
}
ListNode common = null;
while(a.size()>0 && b.size()>0 && a.peek() == b.peek()){
common = a.pop();
b.pop();
}
return common;
}
// 数学方法,双指针相遇
A走完到common, a+b-c
B走完到common, b+a-c
相等的的点就是重合的点
ListNode getIntersectionNode(ListNode headA, ListNode headB) {
ListNode a = headA;
ListNode b = headB;
while(a!=b){
a = a == null?headB:a.next;
b = b == null?headA:b.next;
}
return a;
}
三、栈
1.从头到尾打印链表力扣
public int[] reversePrint(ListNode head) {
if(head == null){
return new int[0];
}
Stack<ListNode> stack = new Stack<>();
while(head!=null){
stack.push(head);
head = head.next;
}
int[] vals = new int[stack.size()];
for(int i = 0; i<vals.length; i++){
vals[i] = stack.pop().val;
}
return vals;
}
两个栈实现队列力扣
class CQueue {
public static Stack<Integer> stack1;
public static Stack<Integer> stack2;
public CQueue() {
stack1 = new Stack<>();
stack2 = new Stack<>();
}
public void appendTail(int value) {
stack1.push(value);
}
public int deleteHead() {
if (stack2.size() == 0) {
while (stack1.size() > 0) {
stack2.push(stack1.pop());
}
}
return stack2.size() > 0 ? stack2.pop() : -1;
}
}
包含min函数的栈 力扣
class MinStack {
Stack<Integer> mainStack;
Stack<Integer> minStack;
/**
* initialize your data structure here.
*/
public MinStack() {
mainStack = new Stack<>();
minStack = new Stack<>();
}
public void push(int x) {
mainStack.push(x);
if (minStack.empty() || x <= minStack.peek()) {
minStack.push(x);
}
}
public void pop() {
int x = mainStack.pop();
if (minStack.size() > 0 && minStack.peek() == x) {
minStack.pop();
}
}
public int top() {
return mainStack.peek();
}
public int min() {
return minStack.peek();
}
}
堆
剑指 Offer 41. 数据流中的中位数
class MedianFinder {
private PriorityQueue<Integer> maxQ;
private PriorityQueue<Integer> minQ;
/**
* initialize your data structure here.
*/
public MedianFinder() {
maxQ = new PriorityQueue<>((o1, o2) -> o2 - o1);
minQ = new PriorityQueue<>((o1, o2) -> o1 - o2);
}
public void addNum(int num) {
if (maxQ.size() == 0) {
maxQ.offer(num);
return;
}
if (num >= maxQ.peek()) {
minQ.offer(num);
}else{
maxQ.offer(num);
}
while (maxQ.size() - minQ.size() > 1) {
minQ.offer(maxQ.poll());
}
while (minQ.size() - maxQ.size() > 1) {
maxQ.offer(minQ.poll());
}
}
public double findMedian() {
if (minQ.size() == maxQ.size() && maxQ.size() != 0) {
return (minQ.peek() + maxQ.peek()) / 2.0;
} else {
return minQ.size() > maxQ.size() ? minQ.peek() : maxQ.peek();
}
}
}
栈的压入弹出
public boolean validateStackSequences(int[] pushed, int[] popped) {
if(pushed == null || popped == null || pushed.length != popped.length){
return false;
}
Stack<Integer> mockStack = new Stack<>();
int cursorP = 0;
for(int i = 0; i<pushed.length; i++){
mockStack.push(pushed[i]);
while(mockStack.size()>0 && cursorP<popped.length && mockStack.peek() == popped[cursorP]){
mockStack.pop();
cursorP++;
}
}
return mockStack.empty();
}
队列
队列最大值(类滑动窗口)
剑指 Offer 59 - II. 队列的最大值
class MaxQueue {
LinkedList<Integer> mainQueue;
LinkedList<Integer> maxQueue;
public MaxQueue() {
mainQueue = new LinkedList<Integer>();
maxQueue = new LinkedList<Integer>();
}
public int max_value() {
if (maxQueue.size() == 0) {
return -1;
}
return maxQueue.peek();
}
public void push_back(int value) {
while (maxQueue.size() > 0 && mainQueue.getLast() < value) {
maxQueue.removeLast();
}
mainQueue.offer(value);
}
public int pop_front() {
int value = mainQueue.isEmpty() ? -1 : mainQueue.poll();
if (maxQueue.size() != 0 && mainQueue.peek() == value) {
maxQueue.poll();
}
return value;
}
public static void main(String args[]) {
MaxQueue maxQueue = new MaxQueue();
maxQueue.push_back(1);
maxQueue.push_back(2);
maxQueue.max_value();
maxQueue.pop_front();
maxQueue.max_value();
}
}
数组
剑指 Offer 21. 调整数组顺序使奇数位于偶数前面 力扣
public int[] exchange(int[] nums) {
if (nums == null || nums.length <= 1) {
return nums;
}
int i = 0;
int j = nums.length - 1;
while (i < j) {
while (i < j && nums[i] % 2 == 1) {
i++;
}
while (i < j && nums[j] % 2 == 0) {
j--;
}
int tmp = nums[i];
nums[i] = nums[j];
nums[j] = tmp;
}
return nums;
}
和为s的两个数字 力扣
public int[] twoSum(int[] nums, int target) {
if (nums == null || nums.length == 0) {
return null;
}
int left = 0;
int right = nums.length - 1;
while (left < right) {
if (target == nums[left] + nums[right]) {
return new int[] {nums[left], nums[right]};
} else if (target < nums[left] + nums[right]) {
right--;
} else {
left++;
}
}
return null;
}
滑动窗口最大值
public int[] maxSlidingWindow(int[] nums, int k) {
if (nums == null || nums.length == 0) {
return null;
}
List<Integer> maxList = new ArrayList<>();
// 队列用于记录最后最大值
LinkedList<Integer> q = new LinkedList<>();
// 用于记录最大值
Integer max = nums[0];
// 窗口未满不用移动
for (int i = 0; i < nums.length && i < k; i++) {
while (q.size() > 0 && nums[i] > q.getLast()) {
q.removeLast();
}
q.add(nums[i]);
max = max > nums[i] ? max : nums[i];
}
maxList.add(max);
// 窗口满了以后,每移动一步,判断窗口开始值前一个(i-k)是否为最大值,是则移除
for (int i = k; i < nums.length; i++) {
while (q.size() > 0 && nums[i] > q.getLast()) {
q.removeLast();
}
q.add(nums[i]);
max = max > nums[i] ? max : nums[i];
if (nums[i - k] == max) {
q.removeFirst();
max = q.getFirst();
}
maxList.add(max);
}
int a[] = new int[maxList.size()];
for (int i = 0; i < maxList.size(); i++) {
a[i] = maxList.get(i);
}
return a;
}
螺旋打印数组力扣
public int[] spiralOrder(int[][] matrix) {
if (matrix == null || matrix.length == 0) {
return new int[0];
}
int rowEnd = matrix.length - 1;
int colEnd = matrix[0].length - 1;
int rowBegin = 0;
int colBegin = 0;
int r = 0;
int c = 0;
int[] result = new int[matrix.length * matrix[0].length];
int cursor = 0;
while (rowBegin < rowEnd && colBegin < colEnd) {
while (c < colEnd) {
result[cursor++] = matrix[r][c++];
}
while (r < rowEnd) {
result[cursor++] = matrix[r++][c];
}
while (c > colBegin) {
result[cursor++] = matrix[r][c--];
}
while (r > rowBegin) {
result[cursor++] = matrix[r--][c];
}
rowBegin++;
rowEnd--;
colBegin++;
colEnd--;
r = rowBegin;
c = colBegin;
}
if (rowBegin == rowEnd) {
for (c = colBegin; c <= colEnd; c++) {
result[cursor++] = matrix[r][c];
}
} else if(colBegin == colEnd){
for (r = rowBegin; r <= rowEnd; r++) {
result[cursor++] = matrix[r][c];
}
}
return result;
}
数组的重复数字
// 通过哈希表
public int findRepeatNumber(int[] nums) {
if(nums == null || nums.length == 0){
return -1;
}
Set<Integer> numSet = new HashSet<>();
for(int i = 0;i<nums.length;i++){
if(numSet.contains(nums[i])){
return nums[i];
}
numSet.add(nums[i]);
}
return -1;
}
// 交换
public int findRepeatNumber(int[] nums) {
if (nums == null || nums.length == 0) {
return -1;
}
int i = 0;
while (i < nums.length) {
if (nums[i] == i) {
i++;
} else {
if (nums[i] == nums[nums[i]]) {
return nums[i];
}
swap(nums, i, nums[i]);
}
}
return -1;
}
* 排序数组查找数字,个数(二分查找)
public static int search(int[] nums, int target) {
if(nums == null || nums.length == 0){
return 0;
}
// 查找右边界
int end = findBand(nums, target);
// 查找左边界
int begin = findBand(nums, target-1);
return end-begin;
}
public static int findBand(int nums[], int target){
int left = 0;
int right = nums.length-1;
while(left<=right){
int mid = (left+right)/2;
if(nums[mid]<=target){
left = mid+1;
}else {
right = mid-1;
}
}
return left;
}
0-n-1中缺失的数字(二分查找)
剑指 Offer 53 - II. 0~n-1中缺失的数字 - 力扣(LeetCode)
public int missingNumber(int[] nums) {
if(nums == null || nums.length == 0){
return -1;
}
int left = 0;
int right = nums.length-1;
while(left<=right){
int mid = (left+right)/2;
if(nums[mid] == mid){
left = mid+1;
}else{
right = mid-1;
}
}
return left;
}
二维数组中的查找
public boolean findNumberIn2DArray(int[][] matrix, int target) {
if(matrix == null || matrix.length == 0){
return false;
}
int i = matrix[0].length-1;
int j = 0;
while(i>=0 && j<matrix.length){
if(matrix[j][i]>target){
i--;
}else if(matrix[j][i]<target){
j++;
}else {
return true;
}
}
return false;
}
旋转数组最小值(二分查找,注意边界)
public int minArray(int[] numbers) {
if(numbers == null || numbers.length == 0){
return -1;
}
int left = 0;
int right = numbers.length-1;
while(left<right){
int mid = (left+right)/2;
// 大于,一定在右边
if(numbers[mid]>numbers[right]){
left = mid+1;
// 小于,可能为最小值或在左边
}else if(numbers[mid]<numbers[right]){
right = mid;
}else{ // 等于,可能在左边也可能在右边
right--;
}
}
return numbers[right];
}
剑指 Offer 51. 数组中的逆序对
class Solution {
private int count = 0;
public int reversePairs(int[] nums) {
if (nums == null || nums.length == 0) {
return 0;
}
countPairs(nums, 0, nums.length-1);
return count;
}
private void countPairs(int[] nums, int left, int right) {
if (left >= right) {
return;
}
int mid = (left + right) / 2;
countPairs(nums, left, mid);
countPairs(nums, mid + 1, right);
merge(nums, left, mid, right);
}
private void merge(int[] nums, int left, int mid, int right) {
int i = left;
int j = mid + 1;
int[] tmp = new int[right-left+1];
int index = 0;
while (i <= mid && j <= right) {
if(nums[i]<=nums[j]){
tmp[index++] = nums[i++];
}else{
count+=(mid+1-i);
tmp[index++] = nums[j++];
}
}
while(i<=mid){
tmp[index++] = nums[i++];
}
while(j<=right){
tmp[index++] = nums[j++];
}
for(int k = 0; k<tmp.length; k++){
nums[k+left] = tmp[k];
}
}
}
剑指 Offer 40. 最小的k个数
class Solution {
public int[] getLeastNumbers(int[] arr, int k) {
if (arr == null || arr.length == 0 || arr.length <= k) {
return arr;
}
sortk(arr, 0, arr.length - 1, k);
return Arrays.copyOfRange(arr, 0, k);
}
private void sortk(int[] arr, int left, int right, int k) {
int mid = division(arr, left, right, k);
if(mid==k) return;
if(mid<k){
sortk(arr, mid+1, right, k);
}else{
sortk(arr, left, mid-1, k);
}
}
private int division(int[] arr, int left, int right, int k) {
int base = arr[left];
while (left < right) {
while (left < right && base <= arr[right]) {
right--;
}
arr[left] = arr[right];
while (left < right && base > arr[left]) {
left++;
}
arr[right] = arr[left];
}
arr[left] = base;
return left;
}
}
剑指 Offer 39. 数组中出现次数超过一半的数字
public int majorityElement(int[] nums) {
if(nums == null || nums.length == 0){
return 0;
}
int cur = nums[0];
int count = 1;
for(int i = 1;i<nums.length;i++){
if(nums[i]!=cur){
count--;
}else{
count++;
}
if(count==0){
cur = nums[i];
count=1;
}
}
return cur;
}
剑指 Offer 66. 构建乘积数组
public int[] constructArr(int[] a) {
if(a==null || a.length == 0){
return new int[0];
}
int[] a1 = new int[a.length];
int[] a2 = new int[a.length];
a1[0] = 1;
a2[a.length-1] = 1;
for(int i = 1;i<a.length; i++){
a1[i] = a1[i-1]*a[i-1];
}
for(int i = a.length-2; i>=0; i--){
a2[i] = a2[i+1]*a[i+1];
}
int[] result = new int[a.length];
for(int i = 0; i<a.length; i++){
result[i] = a1[i]*a2[i];
}
return result;
}
剑指 Offer 57 - II. 和为s的连续正数序列
public int[][] findContinuousSequence(int target) {
if (target <= 2) {
return new int[0][0];
}
List<int[]> result = new ArrayList<>();
int begin = 1;
int end = 2;
while (end <= (target / 2 + 1) && begin < end) {
int sum = (begin + end) * (end-begin+1) / 2;
if(sum == target) result.add(genList(begin++, end++));
if(sum<target) end++;
if(sum>target) begin++;
}
return result.toArray(new int[result.size()][]);
}
private int[] genList(int begin, int end) {
int[] r = new int[end-begin+1];
for(int i = 0; i<= (end-begin); i++){
r[i] = begin+i;
}
return r;
}
15. 三数之和
public List<List<Integer>> threeSum(int[] nums) {
if (nums == null || nums.length == 0) {
return null;
}
Arrays.sort(nums);
List<List<Integer>> lists = new ArrayList<>();
for (int i = 0; i < nums.length; i++) {
if(i>0&&nums[i-1]==nums[i]){
continue;
}
int begin = i + 1;
int end = nums.length-1;
while (begin < end) {
int sum = nums[begin] + nums[end]+nums[i];
if (sum==0) {
List<Integer> tmp = new ArrayList<>();
tmp.add(nums[i]);
tmp.add(nums[begin]);
tmp.add(nums[end]);
lists.add(tmp);
while(begin<end && nums[begin+1] == nums[begin]){
begin++;
}
begin++;
while(begin<end && nums[end-1]==nums[end]){
end--;
}
end--;
} else if (sum < 0) {
while(begin<end && nums[begin+1] == nums[begin]){
begin++;
}
begin++;
} else if (sum > 0) {
while(begin<end && nums[end-1]==nums[end]){
end--;
}
end--;
}
}
}
return lists;
}
16. 最接近的三数之和
public int threeSumClosest(int[] nums, int target) {
if(nums == null || nums.length == 0){
return target;
}
Arrays.sort(nums);
int best = Integer.MAX_VALUE;
for(int i = 0; i< nums.length; i++){
int begin = i+1;
int end = nums.length-1;
while(begin<end){
int tmp = target-nums[i]-nums[begin]-nums[end];
if(tmp==0){
return target;
}
if(tmp>0){
begin++;
}else if(tmp<0){
end--;
}
best = Math.abs(tmp)>Math.abs(best)?best:tmp;
}
}
return target-best;
}
23. 合并 K 个升序链表
public ListNode mergeKLists(ListNode[] lists) {
if(lists == null ||lists.length == 0){
return null;
}
PriorityQueue<NodeMap> queue = new PriorityQueue<>();
for(int i = 0; i<lists.length; i++){
ListNode tmp = lists[i];
while(tmp!=null){
queue.offer(new NodeMap(tmp.val, tmp));
tmp = tmp.next;
}
}
if(queue.size() == 0){
return null;
}
ListNode head = queue.poll().node;
ListNode tail = head;
while(queue.size()>0){
tail.next = queue.poll().node;
tail = tail.next;
}
// 注意设置null
tail.next = null;
return head;
}
public class NodeMap implements Comparable<NodeMap>{
int val;
ListNode node;
public NodeMap(int val, ListNode node){
this.val = val;
this.node = node;
}
// 继承comparable,和comparator,注意记一下
@Override
public int compareTo(NodeMap o) {
return this.val-o.val;
}
}
数组路径
剑指 Offer 12. 矩阵中的路径
// 遍历每个节点为开始节点,尝试头节点不要写在递归里,会死循环
public boolean exist(char[][] board, String word) {
if(board == null || word == null){
return false;
}
boolean[][] isUsed = new boolean[board.length][board[0].length];
for(int i=0;i<board.length;i++){
for(int j = 0; j<board[0].length;j++){
if(exist(board,i,j,0,word,isUsed)){
return true;
}
}
}
return false;
}
public boolean exist(char[][] board, int i, int j, int w, String word, boolean[][] isUsed){
if(w == word.length()){
return true;
}
if(i<0 || i>=board.length || j<0 || j>=board[0].length || isUsed[i][j] || word.charAt(w) != board[i][j]){
return false;
}
isUsed[i][j] = true;
w=w+1;
boolean result = exist(board, i+1, j, w, word, isUsed)
||exist(board, i, j+1, w, word, isUsed)
||exist(board, i, j-1, w, word, isUsed)
||exist(board, i-1, j, w, word, isUsed);
isUsed[i][j] = false;
return result;
}
private static int[][] pathMap;
public int movingCount(int m, int n, int k) {
pathMap = new int[m][n];
int maxPath = 0;
moving(0,0,k,m,n);
for(int i = 0;i<m;i++){
for(int j = 0; j<n;j++){
if(pathMap[i][j] == 1) maxPath++;
}
}
return maxPath;
}
public void moving(int i, int j, int k, int m, int n){
if(j<0 || j>= m || i<0 || i >= n|| countSum(i)+countSum(j)>k || pathMap[j][i] == 1){
return;
}
pathMap[j][i] = 1;
moving(i+1,j,k,m,n);
moving(i-1,j,k,m,n);
moving(i,j-1,k,m,n);
moving(i,j+1,k,m,n);
}
public int countSum(int i){
int result = 0;
while(i>0){
result += i%10;
i = i/10;
}
return result;
}
树
从上到下打印二叉树
public int[] levelOrder(TreeNode root) {
if(root == null){
return new int[0];
}
List<Integer> valList = new ArrayList<>();
Queue<TreeNode> tmpQueue = new LinkedList<>();
tmpQueue.offer(root);
while(tmpQueue.size()>0){
TreeNode node = tmpQueue.poll();
valList.add(node.val);
if(node.left!=null){
tmpQueue.offer(node.left);
}
if(node.right!=null){
tmpQueue.offer(node.right);
}
}
int[] result = new int[valList.size()];
for(int i = 0; i<valList.size();i++){
result[i] = valList.get(i);
}
return result;
}
从上到下分层打印二叉树
public List<List<Integer>> levelOrder(TreeNode root) {
List<List<Integer>> result = new ArrayList<>();
if(root==null){
return result;
}
Queue<TreeNode> queue1 = new LinkedList<>();
Queue<TreeNode> queue2 = new LinkedList<>();
queue1.offer(root);
List<Integer> tmp = new ArrayList<>();
while(queue1.size()>0){
TreeNode curNode = queue1.poll();
tmp.add(curNode.val);
if(curNode.left!=null){
queue2.offer(curNode.left);
}
if(curNode.right!=null){
queue2.offer(curNode.right);
}
if(queue1.size() == 0){
result.add(tmp);
tmp = new ArrayList<>();
Queue<TreeNode> tmpp = queue1;
queue1 = queue2;
queue2 = tmpp;
}
}
return result;
}
* 之字形分层打印二叉树 力扣
public List<List<Integer>> levelOrder(TreeNode root) {
List<List<Integer>> result = new ArrayList<>();
if(root == null){
return result;
}
// 注意此处申明,必须是LinkedList才有addFirst方法!!
LinkedList<Integer> tmpResult = new LinkedList<>();
boolean flag = true;
Queue<TreeNode> queue1 = new LinkedList<>();
Queue<TreeNode> queue2 = new LinkedList<>();
queue1.offer(root);
while(queue1.size()>0){
// 之字形,在上一题的基础上,反正打印就行了,不用转换队列写入顺序!!
root = queue1.poll();
if(flag){
tmpResult.add(root.val);
}else{
tmpResult.addFirst(root.val);
}
if(root.left!=null){
queue2.offer(root.left);
}
if(root.right!=null){
queue2.offer(root.right);
}
if(queue1.size() == 0){
result.add(tmpResult);
tmpResult = new LinkedList<>();
queue1 = queue2;
queue2 = new LinkedList<>();
flag = !flag;
}
}
return result;
}
* 判断子树 力扣
public boolean isSubStructure(TreeNode A, TreeNode B) {
// 由于约定空树不为子树,因此需为不等于null,三种可能:相同树,为右子树,为左子树
return (A!=null && B!=null &&(isSameTree(A, B)||isSubStructure(A.left, B)||isSubStructure(A.right, B)));
}
// 相同树,
public boolean isSameTree(TreeNode A, TreeNode B) {
// B为null,说明递归到头了,返回true
if(B == null){
return true;
}
// 否则,A为null,说明A cover不住B,返回false
if(A == null || A.val!=B.val){
return false;
}
// 循环判断左右节点
return isSameTree(A.left, B.left) && isSameTree(A.right, B.right);
}
剑指 Offer 27. 二叉树的镜像
public TreeNode mirrorTree(TreeNode root) {
if(root == null){
return null;
}
TreeNode tmp = root.left;
root.left = mirrorTree(root.right);
root.right = mirrorTree(tmp);
return root;
}
剑指 Offer 28. 对称的二叉树 - 力扣(LeetCode)
public boolean isSymmetric(TreeNode root) {
if(root == null){
return true;
}
return isSymmetric(root.left, root.right);
}
// 判断节点1的左节点和节点2的右节点,节点1的右节点和节点2的左节点是否相等。相等则两颗树是对称的
// 此方法为判断两个节点对应的树是否对称
public boolean isSymmetric(TreeNode left, TreeNode right){
if(left == null && right == null){
return true;
}
if(left == null || right == null){
return false;
}
return left.val == right.val && isSymmetric(left.left, right.right)
&& isSymmetric(left.right, right.left);
}
剑指 Offer 34. 二叉树中和为某一值的路径
private List<List<Integer>> paths;
public List<List<Integer>> pathSum(TreeNode root, int target) {
paths = new ArrayList<>();
if(root == null){
return paths;
}
findPath(root, target, new ArrayList<>());
return paths;
}
public void findPath(TreeNode root, int target, List<Integer> path){
if(root == null){
return;
}
target = target-root.val;
path.add(root.val);
if(target == 0 && root.left == null && root.right == null){
paths.add(new ArrayList<>(path));
}else{
findPath(root.left, target, path);
findPath(root.right, target, path);
}
path.remove(path.size()-1);
}
剑指 Offer 36. 二叉搜索树与双向链表
private Node preNode;
private Node head;
public Node treeToDoublyList(Node root) {
if (root == null) {
return null;
}
// 中序遍历搜索二叉树
iterateNode(root);
head.left = preNode;
preNode.right = head;
return head;
}
public Node iterateNode(Node node) {
if (node.left != null) {
iterateNode(node.left);
}
// 当前节点的左节点指向前一个节点,前一个结点的右节点指向当前节点
node.left = preNode;
if(preNode == null){
head = node;
}else{
preNode.right = node;
}
preNode = node;
if (node.right != null) {
iterateNode(node.right);
}
return node;
}
剑指 Offer 54. 二叉搜索树的第k大节点
// 第k大,就是倒排第k个!!
class Solution {
int count = 0;
int result = 0;
public int kthLargest(TreeNode root, int k) {
if(root == null){
return result;
}
kthLargest(root.right, k);
count++;
if(k == count){
result = root.val;
return result;
}
kthLargest(root.left, k);
return result;
}
}
剑指 Offer 55 - I. 二叉树的深度
class Solution {
private int max = 0;
public int maxDepth(TreeNode root) {
if(root == null){
return 0;
}
dfs(root, 0);
return max;
}
public void dfs(TreeNode node, int n){
if(node == null){
max = max<n?n:max;
return;
}
dfs(node.left, n+1);
dfs(node.right, n+1);
}
}
boolean balance = true;
public boolean isBalanced(TreeNode root) {
if(root == null){
return balance;
}
dfs(root);
return balance;
}
public int dfs(TreeNode root){
if(root==null){
return 0;
}
int leftLength = dfs(root.left)+1;
int rightLength = dfs(root.right)+1;
if(Math.abs(leftLength-rightLength)>1){
balance = false;
}
return Math.max(leftLength, rightLength);
}
剑指 Offer 68 - I. 二叉搜索树的最近公共祖先
public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
if(root == null || p == null || q == null){
return null;
}
if((root.val-p.val)*(root.val-q.val)<=0){
return root;
}
if(root.val<p.val){
return lowestCommonAncestor(root.right, p, q);
}else{
return lowestCommonAncestor(root.left,p, q);
}
}
剑指 Offer 68 - II. 二叉树的最近公共祖先
// 找到节点后,向上递归
public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
if(root == null || root == p || root == q){
return root;
}
TreeNode left = lowestCommonAncestor(root.left, p, q);
TreeNode right = lowestCommonAncestor(root.right, p, q);
if(left == null && right == null){
return null;
}
if(left == null){
return right;
}
if(right == null){
return left;
}
// left,right都不为null,说明是最近公共节点
return root;
}
public class Codec {
private static final String NULL_MARK = "#";
private StringBuilder sb;
private int cursor;
// Encodes a tree to a single string.
public String serialize(TreeNode root) {
sb = new StringBuilder();
serializeDfs(root);
String sbr = sb.toString();
return sbr.length() == 0 ? null : sbr.substring(0, sbr.length() - 1);
}
private void serializeDfs(TreeNode root) {
if (root == null) {
sb.append(NULL_MARK);
sb.append(",");
return;
}
sb.append(root.val);
sb.append(",");
serializeDfs(root.left);
serializeDfs(root.right);
}
// Decodes your encoded data to tree.
public TreeNode deserialize(String data) {
cursor = 0;
if (data == null || data.length() == 0) {
return null;
}
return deserializeDfs(data.split(","));
}
private TreeNode deserializeDfs(String[] data) {
if (cursor >= data.length || data[cursor].equals(NULL_MARK)) {
cursor++;
return null;
}
TreeNode node = new TreeNode(Integer.parseInt(data[cursor]));
cursor++;
node.left = deserializeDfs(data);
node.right = deserializeDfs(data);
return node;
}
}
剑指 Offer 07. 重建二叉树
class Solution {
private int index;
public TreeNode buildTree(int[] preorder, int[] inorder) {
if (preorder == null || inorder == null || preorder.length != inorder.length) {
return null;
}
index = 0;
return buildTree(preorder, inorder, 0, preorder.length - 1);
}
public TreeNode buildTree(int[] preorder, int[] inorder, int begin, int end) {
if (begin > end) {
return null;
}
int val = preorder[index++];
int mid = findMid(val, inorder, begin, end);
if (mid < 0) {
return null;
}
TreeNode curNode = new TreeNode(val);
curNode.left = buildTree(preorder, inorder, begin, mid - 1);
curNode.right = buildTree(preorder, inorder, mid + 1, end);
return curNode;
}
private int findMid(int val, int[] inorder, int begin, int end) {
for (int i = begin; i <= end; i++) {
if (val == inorder[i]) {
return i;
}
}
return -1;
}
}
剑指 Offer 33. 二叉搜索树的后序遍历序列
public boolean verifyPostorder(int[] postorder) {
if (postorder == null || postorder.length == 0) {
return true;
}
return verifyPostOrder(postorder, 0, postorder.length - 1);
}
public boolean verifyPostOrder(int[] postorder, int begin, int end) {
if (begin >= end) {
return true;
}
int root = postorder[end];
int leftRoot = findLeftRoot(postorder, begin, end - 1, root);
for (int i = leftRoot + 1; i < end; i++) {
if (postorder[i] < root) {
return false;
}
}
return (leftRoot < begin ? true : verifyPostOrder(postorder, begin, leftRoot))
&& (leftRoot + 1 >= end ? true : verifyPostOrder(postorder, leftRoot + 1, end - 1));
}
private int findLeftRoot(int[] postorder, int begin, int end, int root) {
for (int i = begin; i <= end; i++) {
if (root < postorder[i]) {
return i - 1;
}
}
return end;
}
数学·运算符
剑指 Offer 64. 求1+2+…+n
// 利用短路和等于号的传递
public int sumNums(int n) {
boolean a = n==1 || (n = n+sumNums(n-1)) == 1;
return n;
}
剑指 Offer 16. 数值的整数次方
// 从大到小算,递归, 注意,n为最小int值时,-会失效
public double myPow(double x, int n) {
long b = n;
if (n >= 0) {
return pow(x, b);
}
else {
return 1 / pow(x, -b);
}
}
public double pow(double x, long n) {
if (n == 0) {
return 1;
}
if (n == 1) {
return x;
}
double cal = pow(x, n / 2);
return cal * cal * (n % 2 == 1 ? x : 1);
}
// 从小到大算,避免递归
public double myPow(double x, int n) {
x = n < 0 ? 1 / x : x;
long b = n;
b = (b>= 0) ? b : -b;
double res = 1.0;
while (b > 0) {
res = res * ((b & 1) == 1 ? x : 1.0);
x = x*x; //相当于翻倍
b = b/2;
}
return res;
}
剑指 Offer 17. 打印从1到最大的n位数
// 一种方案,直接算最大值,循环输出
public int[] printNumbers(int n) {
int max = 1;
while(n>0){
max = 10*max;
n--;
}
int[] result = new int[max-1];
for(int i = 1;i<max;i++){
result[i-1] = i;
}
return result;
}
// 另一种方案,递归,可用String输出大数
private List<String> result = new ArrayList<>();
public List<String> printNumbers(int n) {
calNumbers(n, "");
result.remove("");
return result;
}
public void calNumbers(int n, String value) {
if (n == 0) {
result.add(value);
return;
}
for (int i = 0; i <= 9; i++) {
// 过滤以0为开头的情况
if (value.equals("") && i == 0) {
calNumbers(n - 1, value);
}else{
calNumbers(n - 1, value + i);
}
}
}
剑指 Offer 45. 把数组排成最小的数
public String minNumber(int[] nums) {
if (nums == null || nums.length == 0) {
return null;
}
String[] nStrs = new String[nums.length];
for (int i = 0; i < nums.length; i++) {
nStrs[i] = String.valueOf(nums[i]);
}
// 注意此处lambda表达式写法
// Arrays.sort(strs, new Comparator<String>() {
// public int compare(String o1, String o2) {
// return (o1+o2).compareTo(o2+o1);
// }
// });
Arrays.sort(nStrs, (s1, s2) -> (s1 + s2).compareTo(s2 + s1));
StringBuilder sb = new StringBuilder();
for (int i = 0; i < nStrs.length; i++) {
sb.append(nStrs[i]);
}
return sb.toString();
}
剑指 Offer 61. 扑克牌中的顺子
public boolean isStraight(int[] nums) {
if (nums == null || nums.length == 0) {
return false;
}
Set<Integer> sets = new HashSet<>();
int zeros = 0;
int min = 14;
int max = 0;
for (int i = 0; i < nums.length; i++) {
if (nums[i] == 0) {
zeros++;
} else {
min = Math.min(min, nums[i]);
max = Math.max(max, nums[i]);
// 判断重复数字
if (sets.contains(nums[i])) {
return false;
}
sets.add(nums[i]);
}
}
// (如果最大值减最小值的差值,减去非0数字的个数的个数)->缺的个数,小于0的个数,那为真
if (((max - min + 1) - (nums.length - zeros)) <= zeros) {
return true;
}
return false;
}
动态规划
剑指 Offer 10- I. 斐波那契数列
public int fib(int n) {
if(n == 0) return 0;
int a = 0;
int b = 1;
for (int i = 2; i <= n; i++) {
int sum = (a + b) % 1000000007;
a = b;
b = sum;
}
return b;
}
剑指 Offer 10- II. 青蛙跳台阶问题
public int numWays(int n) {
if(n == 0){
return 1;
}
int a = 1;
int b = 1;
for(int i = 2; i<=n; i++){
int tmp = b;
b = (a+b)%1000000007;
a = tmp;
}
return b;
}
剑指 Offer 63. 股票的最大利润
// 若前面所有数组只和大于0,则保留,遍历找到最大值
public int maxProfit(int[] prices) {
if(prices == null || prices.length == 0){
return 0;
}
int max = 0;
int sum = 0;
for(int i = 1; i < prices.length; i++){
sum += prices[i]-prices[i-1];
if(sum<0) {
sum = 0;
}else{
max = Math.max(sum, max);
}
}
return max;
}
// 遍历,找到当前节点前一个最小的cost, 比较当前节点和最小cost的差值,取最大
public int maxProfit(int[] prices) {
if (prices == null || prices.length == 0) {
return 0;
}
int a = 0;
int cost = prices[0];
for (int i = 1; i < prices.length; i++) {
cost = Math.min(prices[i-1], cost);
a = Math.max(a, prices[i] - cost);
}
return a;
}
剑指 Offer 42. 连续子数组的最大和
public int maxSubArray(int[] nums) {
if (nums == null || nums.length == 0) {
return 0;
}
int max = nums[0];
int count = nums[0];
for (int i = 1; i < nums.length; i++) {
if (count < 0) {
count = 0;
}
count += nums[i];
max = Math.max(count, max);
}
return max;
}
剑指 Offer 47. 礼物的最大价值
public int maxValue(int[][] grid) {
if (grid == null || grid.length == 0) {
return 0;
}
int[][] result = new int[grid.length][grid[0].length];
for (int i = 0; i < grid.length; i++) {
for (int j = 0; j < grid[0].length; j++) {
if (i == 0 && j == 0) {
result[i][j] = grid[i][j];
} else if (i == 0) {
result[i][j] = result[i][j - 1] + grid[i][j];
} else if (j == 0) {
result[i][j] = result[i - 1][j] + grid[i][j];
} else {
result[i][j] = Math.max(result[i - 1][j], result[i][j - 1]) + grid[i][j];
}
}
}
return result[grid.length - 1][grid[0].length - 1];
}
剑指 Offer 46. 把数字翻译成字符串
public int translateNum(int num) {
String numStr = String.valueOf(num);
return transCount(numStr, numStr.length() - 1);
}
private int transCount(String numStr, int index) {
if (index <= 0) {
return 1;
}
int twoCharacterNum = Integer.parseInt(numStr.substring(index - 1, index + 1));
return (twoCharacterNum < 26 && twoCharacterNum > 9) ?
transCount(numStr, index - 1)
+ transCount(numStr, index - 2)
: transCount(numStr, index - 1);
}
剑指 Offer 48. 最长不含重复字符的子字符串
public int lengthOfLongestSubstring(String s) {
if (s == null || s.length() == 0) {
return 0;
}
int maxL = 0;
Queue<Character> q = new LinkedList<>();
Set<Character> sets = new HashSet<>();
for (int i = 0; i < s.length(); i++) {
if (sets.contains(s.charAt(i))) {
Character tmp = q.poll();
while (tmp != s.charAt(i)) {
sets.remove(tmp);
tmp = q.poll();
}
sets.remove(tmp);
}
sets.add(s.charAt(i));
q.offer(s.charAt(i));
maxL = Math.max(sets.size(), maxL);
}
return maxL;
}
public int lengthOfLongestSubstring(String s) {
if (s == null || s.length() == 0) {
return 0;
}
Map<Character, Integer> indexMap = new HashMap<>();
int maxLen = 0;
int left = 0;
for (int i = 0; i < s.length(); i++) {
char cur = s.charAt(i);
if (indexMap.containsKey(cur)) {
left = Math.max(left,indexMap.get(cur) + 1);
}
indexMap.put(cur, i);
maxLen = i - left + 1 > maxLen ? i - left + 1 : maxLen;
}
return maxLen;
}
**剑指 Offer 19. 正则表达式匹配
public boolean isMatch(String s, String p) {
if (s == null || p == null) {
return true;
}
int m = s.length() + 1;
int n = p.length() + 1;
boolean[][] dp = new boolean[m][n];
dp[0][0] = true;
// s长度为0时,p是否匹配
for (int j = 1; j < p.length(); j += 2) {
dp[0][j+1] = dp[0][(j+1)-2] && p.charAt(j) == '*';
}
// 动态规划考虑的是到i长度s和j长度的p是否匹配
for (int i = 1; i < m; i++) {
for (int j = 1; j < n; j++) {
if (p.charAt(j - 1) == '*') {
// 1.为a*号,且无a 2.为a*号,有a
dp[i][j] = dp[i][j - 2] || dp[i - 1][j] && (s.charAt(i - 1) == p.charAt(j - 2) || p.charAt(j - 2) == '.');
} else {
// 3.无*号
dp[i][j] = dp[i - 1][j - 1] && (s.charAt(i - 1) == p.charAt(j - 1) || p.charAt(j - 1) == '.');
}
}
}
return dp[m-1][n-1];
}
剑指 Offer 49. 丑数
public int nthUglyNumber1(int n) {
int a2 = 0, a3 = 0, a5 = 0;
int[] dp = new int[n];
dp[0] = 1;
// a2,a3,a5代表当前i是否乘过2,3,5
for (int i = 1; i <= n-1; i++) {
dp[i] = Math.min(Math.min(dp[a2] * 2, dp[a3] * 3), dp[a5] * 5);
if (dp[i] == dp[a2] * 2) {
a2++;
}
if (dp[i] == dp[a3] * 3) {
a3++;
}
if (dp[i] == dp[a5] * 5) {
a5++;
}
}
return dp[n-1];
}
**剑指 Offer 60. n个骰子的点数
// j表示上一次的累计和,k表示这一次投的值
public double[] dicesProbability(int n) {
double[] bp = new double[6];
Arrays.fill(bp, 1.0/6.0);
for(int i=2;i<=n;i++){
double[] tmp = new double[i*6-i+1];
for(int j = 0;j<bp.length;j++){
for(int k = 0; k<6; k++){
tmp[j+k] += bp[j]*(1.0/6.0);
}
}
bp = tmp;
}
return bp;
}
剑指 Offer 14- I. 剪绳子
public int cuttingRope(int n) {
if (n <= 1) {
return n;
}
if (n == 2) {
return 1;
}
if (n == 3) {
return 2;
}
int[] value = new int[n + 1];
// 动态规划,写入每个拆分原子的最大拆分值
for (int i = 1; i <= n; i++) {
int max = i;
for (int j = 1; j < i/2+1; j++) {
int otherUnit = value[i - j];
max = otherUnit * j > max ? otherUnit * j : max;
}
value[i] = max;
}
return value[n];
}
运算符
汉明码
剑指 Offer 15. 二进制中1的个数
// >> 右移 ; >>>无符号右移!
public int hammingWeight(int n) {
int count = 0;
while(n!=0){
if((n&1)==1) count++;
n = n>>>1;
}
return count;
}
剑指 Offer 65. 不用加减乘除做加法
public int add(int a, int b) {
while(b!=0){
int n = a^b;
b = (a&b)<<1;
a = n;
}
return a;
}
剑指 Offer 56 - I. 数组中数字出现的次数
// 注意运算符优先级
public int[] singleNumbers(int[] nums) {
int m = 0;
for(int i= 0; i<nums.length;i++){
m ^= nums[i];
}
int count = 1;
while((m&1)!=1){
count = count<<1;
m = m>>1;
}
int[] result = new int[2];
for(int i = 0; i<nums.length;i++){
if((nums[i]&count) != 0){
result[1] ^= nums[i];
}else{
result[0]^=nums[i];
}
}
return result;
}
剑指 Offer 56 - II. 数组中数字出现的次数 II
// 运算符优先级不确定,就加括号!!!!
public int singleNumber(int[] nums) {
int[] bucket = new int[32];
for(int i= 0;i<nums.length; i++){
int j = 31;
while(nums[i]!=0){
bucket[j--] += nums[i]&1;
nums[i] = nums[i]>>>1;
}
}
int result = 0;
for(int i=0;i<32;i++){
result = (result<<1) + bucket[i]%3;
}
return result;
}
29. 两数相除
public int divide(int dividend, int divisor) {
// 注意前置判断
if(divisor == 0){
return 0;
}
if(dividend == Integer.MIN_VALUE && divisor == -1){
return Integer.MAX_VALUE;
}
if(dividend == Integer.MIN_VALUE && divisor == 1){
return Integer.MIN_VALUE;
}
// 用负数算
boolean isRev = false;
if(dividend>0){
isRev = !isRev;
dividend = -dividend;
}
if(divisor>0){
isRev = !isRev;
divisor = -divisor;
}
// 用两个数组存储成倍的除数
int count = 0;
int x = 1;
List<Integer> dNum = new ArrayList<>();
List<Integer> dNex = new ArrayList<>();
int maxDNum = divisor;
while(maxDNum>=dividend && maxDNum<0){
dNum.add(maxDNum);
dNex.add(x);
x = x<<1;
maxDNum = maxDNum<<1;
}
for(int i = dNum.size()-1; i>=0; i--){
if(dNum.get(i)>=dividend){
count += dNex.get(i);
dividend -= dNum.get(i);
}
}
return isRev?-count:count;
}
}
数学
剑指 Offer 14- II. 剪绳子 II
// 特别注意数据类型!!!
public int cuttingRope(int n) {
if (n <= 3) {
return n - 1;
}
int cut = n / 3;
int left = n % 3;
if (left == 1) {
cut = cut - 1;
}
long max = left == 0 ? 1 : left == 1 ? 4 : 2;
for (int i = 1; i <= cut; i++) {
max = max*3%1000000007;
}
return (int)max;
}
剑指 Offer 62. 圆圈中最后剩下的数字
// 常规写法
public int lastRemaining(int n, int m) {
if(n == 0 || m == 0){
return n;
}
int cur = -1;
List<Integer> list = new ArrayList<>();
for(int i = 0;i<n;i++){
list.add(i);
}
while(list.size()>1){
cur = cur+m;
cur = cur%(list.size());
list.remove(cur);
cur--;
}
return list.get(0);
}
// 数学写法,没看懂
public int lastRemaining(int n, int m) {
if(n == 0 || m == 0){
return n;
}
int count = 0;
for(int i = 1; i<=n; i++){
count = (count+(m%i))%i;
}
return count;
}
剑指 Offer 43. 1~n 整数中 1 出现的次数
public int countDigitOne(int n) {
int digit = 1;
int cur = n%10;
int low = 0;
int high = n/10;
int result = 0;
while(cur!=0 || high!=0){
// cur 为0, 高位*位数
if(cur == 0){
result += high*digit;
// cur为1,高位*位数+(1+低位)
}else if(cur == 1){
result += (high)*digit+low+1;
}else if(cur>1){
// cur大于1, (高位+1)*位数
result += (high+1)*digit;
}
low += cur*digit;
cur = high%10;
high = high/10;
digit = digit*10;
}
return result;
}
剑指 Offer 44. 数字序列中某一位的数字
// 很一般的算法,注意都用long
public int findNthDigit(int n) {
long nn = (long)n+1;
long digit = 1;
long mul = 1;
long count = 1;
// 计算位数
while(count<nn){
count+=9*digit*mul;
digit++;
mul = mul*10;
}
digit--;
mul = mul/10;
// 找到开始的位置
count = count-mul*digit*9;
long left = nn-count;
long begin = mul-1;
begin = begin+left/digit;
if(left%digit == 0){
String str = String.valueOf(begin);
return str.charAt(str.length()-1)-'0';
}else{
String str = String.valueOf(begin+1);
return str.charAt((int)(left%digit-1))-'0';
}
}