劍指Offer
Day 1
3. 數組中重複的數字
解題關鍵: 數組nums的長度爲n,且裏面的數字取值範圍是[0, n-1]。
思路一
通過開闢一個新的數組array,array的index代表nums數組中的值,array的值代表nums數組中元素出現的次數。
複雜度: 時間複雜度:O(n)
,空間複雜度:O(n)
public int findRepeatNumber(int[] nums) {
int[] array = new int[nums.length];
int ans = -1;
for(int i = 0; i < nums.length; i++) {
array[nums[i]] += 1;
if (array[nums[i]] > 1) {
ans = nums[i];
}
}
return ans;
}
思路二
通過交換,讓每個數字出現在其應該出現的位置
複雜度: 時間複雜度:O(n)
,空間複雜度:O(1)
public int findRepeatNumber(int[] nums) {
boolean flag = false;
int ans = -1;
for (int i = 0; i < nums.length; i++) {
if (nums[i] == i) {
continue;
}
while (nums[i] != i) {
if (nums[i] == nums[nums[i]]) {
ans = nums[i];
flag = true;
break;
} else {
int tmp = nums[i];
nums[i] = nums[nums[i]];
nums[tmp] = tmp;
}
}
if (flag == true) {
break;
}
}
return ans;
}
4. 二維數組中的查找
二維數組的規律:每一行都按照從左到右遞增的順序排序,每一列都按照從上到下遞增的順序排序。
思路一
利用規律進行查找元素:從右上角開始,假設要查找的數字是x。
- 如果當前數字大於x,那麼就向左查找;
- 如果當前數字小於x,那麼就向下查找;
- 如果當前數字等於x,那麼就查找完畢,返回true;
- 如果越界了,那麼查找完畢,返回false。
複雜度: 時間複雜度:O(n)
,空間複雜度:O(1)
public boolean findNumberIn2DArray(int[][] matrix, int target) {
if (matrix == null || matrix.length == 0 || matrix[0].length == 0) {
return false;
}
int width = matrix[0].length;
int height = matrix.length;
int row = 0;
int col = width - 1;
boolean ans = false;
while (row < height && col >= 0) {
if (matrix[row][col] == target) {
ans = true;
break;
} else if (matrix[row][col] > target) {
col--;
} else if (matrix[row][col] < target) {
row++;
}
}
return ans;
}
5. 替換空格
請實現一個函數,把字符串 s
中的每個空格替換成"%20"。
思路一
使用 StringBuilder
,加額外的空間進行組裝
複雜度: 時間複雜度:O(n)
,空間複雜度:O(n)
public String replaceSpace(String 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();
}
思路二
使用char[]數組,該數組的長度設置爲當前長度的3倍,確保即使當前字符串全部是空格,也可以放得下。
複雜度: 時間複雜度:O(n)
,空間複雜度:O(3n)
public String replaceSpace(String s) {
char[] chars = new char[s.length() * 3];
char[] ss = s.toCharArray();
int index = 0;
for (char c : ss) {
if (c == ' ') {
chars[index++] = '%';
chars[index++] = '2';
chars[index++] = '0';
} else {
chars[index++] = c;
}
}
return new String(chars, 0, index);
}
Tips: 將字符數組轉化成String的方式
String(byte[] bytes, int offset, int length);
Constructs a new String
by decoding the specified subarray of bytes using the platform’s default charset.
6. 從尾到頭打印鏈表
思路一:
不考慮時間複雜度或者其他因素,可以直接通過將鏈表壓入棧,緊接着彈出,就可以打印鏈表。
Java中官方JDK不推薦使用Stack。但是就算法題來說,爲了說明算法還是使用了Stack
public int[] reversePrint(ListNode head) {
Stack<Integer> stack = new Stack();
while (head != null) {
stack.push(head.val);
head = head.next;
}
int[] ans = new int[stack.size()];
for (int i = 0; i < ans.length; i++) {
ans[i] = stack.pop();
}
return ans;
}
思路二:
本題還可以採用遞歸實現
遞歸終止條件是:head.next == null;
遞歸的終止操作是將其加入到 ArrayList 中
ArrayList<Integer> list = new ArrayList<>();
public int[] reversePrint(ListNode head) {
recur(head);
int[] ans = new int[list.size()];
for(int i = 0; i < ans.length; i++) {
ans[i] = list.get(i);
}
return ans;
}
public void recur(ListNode head) {
if (head == null) return;
recur(head.next);
list.add(head.val);
}
7. 重建二叉樹
根據二叉樹遍歷的順序,
前序遍歷:按照 根節點-左子樹-右子樹 遍歷
中序遍歷:按照 左子樹-根節點-右子樹 遍歷
思路:
遞歸終止條件: 當 in_st > in_end
時,說明這個時候已經遍歷完了,直接返回null。
構建 node
節點,確定當前遞歸的 node
的 index
即爲 i
。
TreeNode node = new TreeNode(preorder[pre_st]);
int i = map.get(preorder[pre_st]);
遞歸遍歷 node
的左子節點,遞歸遍歷 node
的右子節點。
最後返回 node
。
複雜度: 時間複雜度:O(n)
,空間複雜度:O(n)
HashMap<Integer, Integer> map = new HashMap<>();
public TreeNode buildTree(int[] preorder, int[] inorder) {
for (int i = 0; i < inorder.length; i++) {
map.put(inorder[i], i);
}
return recur(preorder, 0, 0, inorder.length - 1);
}
public TreeNode recur(int[] preorder, int pre_st, int in_st, int in_end) {
if (in_st > in_end) {
return null;
}
TreeNode node = new TreeNode(preorder[pre_st]);
int i = map.get(preorder[pre_st]);
node.left = recur(preorder, pre_st + 1, in_st, i - 1);
node.right = recur(preorder, pre_st + i - in_st + 1, i + 1, in_end);
return node;
}