劍指offer 第一週
acwing-13.找出數組中重複的數字
給定一個長度爲 nn 的整數數組 nums
,數組中所有的數字都在0∼n−1 的範圍內。
數組中某些數字是重複的,但不知道有幾個數字重複了,也不知道每個數字重複了幾次。
請找出數組中任意一個重複的數字。
注意:如果某些數字不在0∼n−1 的範圍內,或數組中不包含重複數字,則返回 -1;
樣例
給定 nums = [2, 3, 5, 4, 3, 2, 6, 7]。
返回 2 或 3。
利用Set存儲;
set 存儲時間複雜度O(1),所以此題的時間複雜度是O(n),空間複雜度是O(n);
class Solution {
public int duplicateInArray(int[] nums) {
Set<Integer> set = new HashSet<>();
int flag = -1;
for(int num : nums){
if(num < 0 || num >= nums.length)return -1;
if(!set.add(num))flag = num;
}
return flag != -1 ? flag : -1;
}
}
利用原數組nums[] 求解
由於while()最多循環n次,故總時間複雜度爲O(3*n)= O(n),空間複雜度O(1);
class Solution {
public int duplicateInArray(int[] nums) {
int n = nums.length;
for(int num : nums)
if(num < 0 || num >= n)
return -1;
for(int i = 0; i < n; i ++){
while(nums[i] != nums[nums[i]])swap(nums,i,nums[i]);
if(i != nums[i])return nums[i];
}
return -1;
}
public void swap(int[] nums, int x,int y){
int temp = nums[x];
nums[x] = nums[y];
nums[y] = temp;
}
}
acwing-14.不修改數組找出重複的數字
給定一個長度爲 n+1 的數組nums
,數組中所有的數均在 1∼n 的範圍內,其中n≥1。
請找出數組中任意一個重複的數,但不能修改輸入的數組。
樣例
給定 nums = [2, 3, 5, 4, 3, 2, 6, 7]。
返回 2 或 3。
思考題:如果只能使用 O(1) 的額外空間,該怎麼做呢?
既然不能修改輸入的數組,也不可以額外開闢空間,故用二分尋找
class Solution {
public int duplicateInArray(int[] nums) {
int n = nums.length;
for(int i = 0; i < n; i ++){
if(binarySearch(nums, nums[i],i)) return nums[i];
}
return -1;
}
public boolean binarySearch(int[] nums, int target,int start){
int l = start + 1, r = nums.length - 1;
while(l < r){
int mid = l + r >>> 1;
if(nums[mid] >= target) r = mid;
else l = mid + 1;
}
return nums[r] == target;
}
}
利用抽屜原理求解: 因爲每個數的範圍在1~n,總數有n+1個,故必定有個位置的數量大於1;
分治:判斷左半邊和右半邊,如果在l~mid的數的個數大於 mid 則代表左邊有重複的數,反之右邊
class Solution {
public int duplicateInArray(int[] nums) {
int n = nums.length;
int l = 1, r = n - 1;
while(l < r){
int mid = l + r >>> 1, s = 0;
for(int x : nums){
if(x >= l && x <= mid)s ++;
}
if(s > mid - l + 1)r = mid;
else l = mid + 1;
}
return r;
}
}
acwing-15.二維數組中的查找
在一個二維數組中,每一行都按照從左到右遞增的順序排序,每一列都按照從上到下遞增的順序排序。
請完成一個函數,輸入這樣的一個二維數組和一個整數,判斷數組中是否含有該整數。
樣例
輸入數組:
[
[1,2,8,9],
[2,4,9,12],
[4,7,10,13],
[6,8,11,15]
]
如果輸入查找數值爲7,則返回true,
如果輸入查找數值爲5,則返回false。
不斷循環,利用二分查找,時間複雜度O(nlogn),空間複雜度O(1);
class Solution {
public boolean searchArray(int[][] array, int target) {
for(int i = 0; i < array.length; i ++){
int len = array[i].length;
if(array[i][0] <= target && array[i][len - 1] >= target && binarySearch(array[i],target))return true;
}
return false;
}
public boolean binarySearch(int[] nums, int target){
int l = 0, r = nums.length - 1;
while(l < r){
int mid = l + r >>> 1;
if(nums[mid] >= target) r = mid;
else l = mid + 1;
}
return nums[r] == target;
}
}
O(n+m) 做法
此題有個規律,就是當前數左邊的數都比 此數要小,下面的數都比次數要大。所以可以每次判斷下target比當前數大還是小.
大的話 往下走,小的話往左走。
class Solution {
public boolean searchArray(int[][] array, int target) {
if(array.length <= 0)return false;
int len = array[0].length;
int x = 0, y = len - 1;
while(x < len && y >= 0 && array[x][y] != target){
if(target < array[x][y]) y--;
else x ++;
}
if(x >= len || y < 0)return false;
return array[x][y] == target;
}
}
16.替換空格
請實現一個函數,把字符串中的每個空格替換成"%20"
。
你可以假定輸入字符串的長度最大是1000。
注意輸出字符串的長度可能大於1000。
樣例
輸入:"We are happy."
輸出:"We%20are%20happy."
java的庫函數實現 – 一行代碼
class Solution {
public String replaceSpaces(StringBuffer str) {
return str.toString().replaceAll(" ","%20");
}
}
17.從頭到尾打印鏈表
輸入一個鏈表的頭結點,按照 從尾到頭 的順序返回節點的值。
返回的結果用數組存儲。
樣例
輸入:[2, 3, 5]
返回:[5, 3, 2]
class Solution {
public int[] printListReversingly(ListNode head) {
List<Integer> res = new ArrayList<>();
while(head != null){
res.add(head.val);
head = head.next;
}
int n = res.size();
int[] ans = new int[n];
for(int i = res.size() - 1; i >= 0 ; i --){
ans[n - i - 1] = res.get(i);
}
return ans;
}
}
18.重建二叉樹
輸入一棵二叉樹前序遍歷和中序遍歷的結果,請重建該二叉樹。
注意:
- 二叉樹中每個節點的值都互不相同;
- 輸入的前序遍歷和中序遍歷一定合法;
樣例
給定:
前序遍歷是:[3, 9, 20, 15, 7]
中序遍歷是:[9, 3, 15, 20, 7]
返回:[3, 9, 20, null, null, 15, 7, null, null, null, null]
返回的二叉樹如下所示:
3
/ \
9 20
/ \
15 7
利用遞歸,前序的第一個點,肯定是根,然後在中序遍歷中找到該點k,則可以知道左子樹的節點個數爲 k - il(il 爲 中序遍歷數組的起點) 將數組分爲左右兩段。
class Solution {
int[] p,i;
Map<Integer,Integer> map;
public TreeNode buildTree(int[] preorder, int[] inorder) {
p = preorder;i = inorder;
map = new HashMap<>();
for(int i = 0 ; i < inorder.length; i ++){
map.put(inorder[i],i);
}
return dfs(0,p.length - 1, 0, i.length - 1);
}
// pl pr 前序遍歷 的下標範圍,il ir 中序遍歷的下標範圍
public TreeNode dfs(int pl,int pr, int il,int ir){
if(pl > pr)return null;
// 前序遍歷的第一個節點是根節點
TreeNode root = new TreeNode(p[pl]);
// 找到該根節點在 中序遍歷的位置
int k = map.get(p[pl]);
// 左子樹 的下標範圍 變成, pl + 1, pl + (k - il) --- 中序遍歷中得到左子樹的數量爲(k - il),
root.left = dfs(pl + 1, pl + k - il, il, k - 1);
// 同理
root.right = dfs(pl + k - il + 1, pr,k + 1,ir);
return root;
}
}
19.二叉樹的下一個節點
給定一棵二叉樹的其中一個節點,請找出中序遍歷序列的下一個節點。
注意:
- 如果給定的節點是中序遍歷序列的最後一個,則返回空節點;
- 二叉樹一定不爲空,且給定的節點一定不是空節點;
樣例
假定二叉樹是:[2, 1, 3, null, null, null, null], 給出的是值等於2的節點。
則應返回值等於3的節點。
解釋:該二叉樹的結構如下,2的後繼節點是3。
2
/ \
1 3
class Solution {
public TreeNode inorderSuccessor(TreeNode p) {
// 當有 右子樹的時候,返回右子樹的最左邊,如果沒有左子樹,即爲本身
if(p.right != null){
p = p.right;
while(p.left != null)p = p.left;
return p;
}
// 當沒有 右子樹的時候,返回其節點是父親的 左子樹節點的父親
while(p.father != null && p == p.father.right)p = p.father;
return p.father;
}
}
20.用兩個棧實現隊列
請用棧實現一個隊列,支持如下四種操作:
- push(x) – 將元素x插到隊尾;
- pop() – 將隊首的元素彈出,並返回該元素;
- peek() – 返回隊首元素;
- empty() – 返回隊列是否爲空;
注意:
- 你只能使用棧的標準操作:
push to top
,peek/pop from top
,size
和is empty
; - 如果你選擇的編程語言沒有棧的標準庫,你可以使用list或者deque等模擬棧的操作;
- 輸入數據保證合法,例如,在隊列爲空時,不會進行
pop
或者peek
等操作;
樣例
MyQueue queue = new MyQueue();
queue.push(1);
queue.push(2);
queue.peek(); // returns 1
queue.pop(); // returns 1
queue.empty(); // returns false
棧的反方向就是隊列,當要pop的時候,將棧A的值,pop給棧B,此時棧B的top就是 隊首了。peek() 同理
class MyQueue {
Stack<Integer> st1;
Stack<Integer> st2;
/** Initialize your data structure here. */
public MyQueue() {
st1 = new Stack<>();
st2 = new Stack<>();
}
/** Push element x to the back of queue. */
public void push(int x) {
if(!st2.isEmpty()){
while(!st2.isEmpty()){
st1.push(st2.pop());
}
}
st1.push(x);
}
/** Removes the element from in front of queue and returns that element. */
public int pop() {
while(!st1.isEmpty()){
st2.push(st1.pop());
}
return st2.pop();
}
/** Get the front element. */
public int peek() {
if(!st2.isEmpty())return st2.peek();
else{
while(!st1.isEmpty()){
st2.push(st1.pop());
}
return st2.peek();
}
}
/** Returns whether the queue is empty. */
public boolean empty() {
if(st1.isEmpty() && st2.isEmpty())return true;
return false;
}
}
21.斐波那契數列
輸入一個整數 nn ,求斐波那契數列的第 nn 項。
假定從0開始,第0項爲0。(nn<=39)
樣例
輸入整數 n=5
返回 5
cur0存儲前前個數,cur0存儲前一個數
class Solution {
public int Fibonacci(int n) {
if(n == 1)return 1;
if(n == 0)return 0;
int cur0 = 1, cur1= 1,ans = 0;
for(int i = 2; i < n; i ++){
ans = cur0 + cur1;
cur0 = cur1;
cur1 = ans;
}
return ans;
}
}
22.旋轉數組的最小數字
把一個數組最開始的若干個元素搬到數組的末尾,我們稱之爲數組的旋轉。
輸入一個升序的數組的一個旋轉,輸出旋轉數組的最小元素。
例如數組{3,4,5,1,2}爲{1,2,3,4,5}的一個旋轉,該數組的最小值爲1。
數組可能包含重複項。
注意:數組內所含元素非負,若數組大小爲0,請返回-1。
樣例
輸入:nums=[2,2,2,0,1]
輸出:0
題意:將一個升序數組,後半部分,放到前面來,求找出最小的元素;
題解: 先保證後部分的數都小於前部分,然後二分找。
class Solution {
public int findMin(int[] nums) {
int n = nums.length - 1;
if(n == -1)return -1;
while(nums[n] == nums[0])n--;
if(nums[n] >= nums[0])return nums[0];
int l = 0, r = n;
while(l < r){
int mid = l + r >>> 1;
if(nums[mid] >= nums[0]) l = mid + 1;
else r = mid;
}
return nums[l];
}
}
23.矩陣中的路徑
請設計一個函數,用來判斷在一個矩陣中是否存在一條包含某字符串所有字符的路徑。
路徑可以從矩陣中的任意一個格子開始,每一步可以在矩陣中向左,向右,向上,向下移動一個格子。
如果一條路徑經過了矩陣中的某一個格子,則之後不能再次進入這個格子。
注意:
- 輸入的路徑不爲空;
- 所有出現的字符均爲大寫英文字母;
樣例
matrix=
[
["A","B","C","E"],
["S","F","C","S"],
["A","D","E","E"]
]
str="BCCE" , return "true"
str="ASAE" , return "false"
dfs 尋找路徑,記得還原標記。
還原與不還原區別: 其他地方到此點時是否有區別,有區別就還原,沒區別,還原個屁
class Solution {
int[] dx = {0,0,1,-1}, dy = {1,-1,0,0};
public boolean dfs(int x,int y,int len, String str,char[][] matrix){
if(str.charAt(len) != matrix[x][y])return false;
if(len + 1 == str.length()) return true;
char t = matrix[x][y];
matrix[x][y] = '#';
for(int i = 0; i < 4; i ++){
int new_x = x + dx[i];
int new_y = y + dy[i];
if(new_x >= 0 && new_x < matrix.length && new_y >= 0 && new_y < matrix[0].length){
if(dfs(new_x,new_y,len + 1,str,matrix))return true;
}
}
matrix[x][y] = t;
return false;
}
public boolean hasPath(char[][] matrix, String str) {
for(int i = 0; i < matrix.length; i++)
for(int j = 0 ; j < matrix[i].length; j++)
if(dfs(i,j,0,str,matrix))return true;
return false;
}
}