1.給定一個排序數組,你需要在原地刪除重複出現的元素,使得每個元素只出現一次,返回移除後數組的新長度。不要使用額外的數組空間,你必須在原地修改輸入數組並在使用 O(1) 額外空間的條件下完成。
輸入[0,0,1,1,1,2,2,3,3,4],
函數應該返回新的長度 5, 並且原數組 nums 的前五個元素被修改爲 0, 1, 2, 3, 4。
思路解析:利用slow=0,fast=1,分別指向數組的第0,1號元素,if(他們相同時),{fast++},else{直到不同時,將slow的下一個元素替換爲fast的元素,slow++,fast++}
public static int removeDuplicates(int[] nums) {
int slow = 0;
for (int fast = 1; fast < nums.length; fast++) {
if (nums[slow] != nums[fast]) {
nums[slow + 1] = nums[fast];
slow++;
}
}
return slow + 1;
}
2.給定一個數組,將數組中的元素向右移動 k 個位置,其中 k 是非負數。
輸入:
[1,2,3,4,5,6,7]
和 k = 3
輸出:
[5,6,7,1,2,3,4]
說明:
.儘可能想出更多的解決方案,至少有三種不同的方法可以解決這個問題。
.要求使用空間複雜度爲 O(1) 的原地算法。
思路解析:元素後移k次就行了,另一種是利用翻轉,任何數組翻轉3次即可,還有一種思路新奇
/**
* 翻轉
* 時間複雜度:O(n)
* 空間複雜度:O(1)
*/
public void rotate_2(int[] nums, int k) {
int n = nums.length;
k %= n;
reverse(nums, 0, n - 1);
reverse(nums, 0, k - 1);
reverse(nums, k, n - 1);
}
private void reverse(int[] nums, int start, int end) {
while (start < end) {
int temp = nums[start];
nums[start++] = nums[end];
nums[end--] = temp;
}
}
/**
* 循環交換
* 時間複雜度:O(n^2/k)
* 空間複雜度:O(1)
*/
public void rotate_3(int[] nums, int k) {
int n = nums.length;
k %= n;
// 第一次交換完畢後,前 k 位數字位置正確,後 n-k 位數字中最後 k 位數字順序錯誤,繼續交換
for (int start = 0; start < nums.length && k != 0; n -= k, start += k, k %= n) {
for (int i = 0; i < k; i++) {
swap(nums, start + i, nums.length - k + i);
}
}
}
3.給定一個整數數組,判斷是否存在重複元素。
如果任何值在數組中出現至少兩次,函數返回 true。如果數組中每個元素都不相同,則返回 false。
輸入: [1,2,3,1]
輸出: true
輸入: [1,2,3,4]
輸出: false
思路解析:我做的是排序然後比較,還有set也比較容易想到,以及map的containKey方法
//Arrays.sort()
public boolean containsDuplicate(int[] nums) {
Arrays.sort(nums);
for(int i=0;i<nums.length-1;i++){
if(nums[i]==nums[i+1]){
return true;
}
}
return false;
}
//if(!set.add())
public boolean containsDuplicate(int[] nums) {
int len = nums.length;
HashSet<Integer> set = new HashSet<>();
for(int i=0;i<len;i++) {
if(!set.add(nums[i]))return true;
}
return false;
}
//map.containsKey
public boolean containsDuplicate(int[] nums) {
HashMap<Integer,Integer>map = new HashMap<>();
for(int i=0;i<nums.length;i++){
if(map.containsKey(nums[i])) return true;
map.put(nums[i],0);
}
return false;
}
//Arrays.stream()
public boolean containsDuplicate(int[] nums) {
Set<Integer> set = new HashSet<>();
Arrays.stream(nums).forEach(set::add);
return nums.length != set.size();
}
4.給定一個非空整數數組,除了某個元素只出現一次以外,其餘每個元素均出現兩次。找出那個只出現了一次的元素。
說明:你的算法應該具有線性時間複雜度。 你可以不使用額外空間來實現嗎?
輸入: [2,2,1]
輸出: 1
思路解析:運用" ^ "運算符,異或的運算方法是一個二進制運算: 1^1=0 ;0^0=0 ;1^0=1 ;0^1=1
public int singleNumber(int[] nums) {
int x=0;
for(int i=0;i<nums.length;i++){
x=x^nums[i];
}
return x;
}
5.給定兩個數組,編寫一個函數來計算它們的交集。
輸入: nums1 = [1,2,2,1], nums2 = [2,2]
輸出: [2,2]
輸入: nums1 = [4,9,5], nums2 = [9,4,9,8,4]
輸出: [4,9]
說明:
. 輸出結果中每個元素出現的次數,應與元素在兩個數組中出現的次數一致。
. 我們可以不考慮輸出結果的順序。
思路解析:排序後利用雙指針同時檢索兩個數組,取出相同的值
public int[] intersect(int[] nums1, int[] nums2) {
Arrays.sort(nums1);
Arrays.sort(nums2);
List<Integer> list = new ArrayList<>();
for (int i = 0, j = 0; i < nums1.length && j < nums2.length; ) {
if (nums1[i] < nums2[j]) {
i++;
} else if (nums1[i] > nums2[j]) {
j++;
} else {
list.add(nums1[i]);
i++;
j++;
}
}
int[] res = new int[list.size()];
for (int i = 0; i < list.size(); i++) {
res[i] = list.get(i);
}
return res;
}
6.給定一個由整數組成的非空數組所表示的非負整數,在該數的基礎上加一。最高位數字存放在數組的首位, 數組中每個元素只存儲一個數字。你可以假設除了整數 0 之外,這個整數不會以零開頭。
輸入: [1,2,3]
輸出: [1,2,4]
解釋: 輸入數組表示數字 123。
思路解析:如果是9,就相當於該位爲0,前一位加1,直到第一位判定是否需要擴容
public int[] plusOne(int[] digits) {
int len = digits.length;
for(int i=len-1;i>=0;i--){
if(digits[i]+1<10){
digits[i]++;
return digits;
}else{
digits[i]=0;
}
}
int[] res=new int[len+1];
res[0]=1;
return res;
}
7.給定一個數組 nums
,編寫一個函數將所有 0
移動到數組的末尾,同時保持非零元素的相對順序。
輸入:
[0,1,0,3,12]
輸出:
[1,3,12,0,0]
說明:
. 必須在原數組上操作,不能拷貝額外的數組。
. 儘量減少操作次數。
思路解析:其他元素前移佔據0的位置,然後再來補0,還有一種就是利用雙指針交換後直接填充0,這裏的if(i!=j)是爲了防止找到0之前i,j相同
public void moveZeroes(int[] nums) {
int i = 0, j = 0;
while (j < nums.length) {
if (nums[j] != 0) {
nums[i++] = nums[j];
}
j++;
}
while (i < nums.length) nums[i++] = 0;
}
//
public void moveZeroes(int[] nums) {
int j=0;
for(int i=0;i<nums.length;i++){
if(nums[i]!=0){
nums[j]=nums[i];
if(i!=j) nums[i]=0;
j++;
}
}
}
8.給定一個整數數組 nums
和一個目標值 target
,請你在該數組中找出和爲目標值的那兩個整數,並返回他們的數組下標。
你可以假設每種輸入只會對應一個答案。但是,你不能重複利用這個數組中同樣的元素。
給定 nums = [2, 7, 11, 15], target = 9
因爲 nums[0] + nums[1] = 2 + 7 = 9
所以返回 [0, 1]
思路解析:雙for暴力破解,最優解是一遍哈希表
public int[] twoSum(int[] nums, int target) {
for(int i=0;i<nums.length-1;i++){
for(int j=i+1;j<nums.length;j++){
if(nums[i]+nums[j]==target){
int[] a=new int[2];
a[0]=i;
a[1]=j;
return a;
}
}
}
return null;
}
//優解
public int[] twoSum(int[] nums, int target) {
Map<Integer,Integer> map=new HashMap<>();
for(int i=0;i<nums.length;i++){
int diff=target-nums[i];
if(map.containsKey(diff)){
return new int[] {map.get(diff),i};
}
map.put(nums[i],i);
}
return null;
}
9.判斷一個 9x9 的數獨是否有效。只需要根據以下規則,驗證已經填入的數字是否有效即可。
- 數字
1-9
在每一行只能出現一次。 - 數字
1-9
在每一列只能出現一次。 - 數字
1-9
在每一個以粗實線分隔的3x3
宮內只能出現一次。
上圖是一個部分填充的有效的數獨。
數獨部分空格內已填入了數字,空白格用 '.'
表示。
輸入:
[
["5","3",".",".","7",".",".",".","."],
["6",".",".","1","9","5",".",".","."],
[".","9","8",".",".",".",".","6","."],
["8",".",".",".","6",".",".",".","3"],
["4",".",".","8",".","3",".",".","1"],
["7",".",".",".","2",".",".",".","6"],
[".","6",".",".",".",".","2","8","."],
[".",".",".","4","1","9",".",".","5"],
[".",".",".",".","8",".",".","7","9"]
]
輸出: true
輸入:
[
["8","3",".",".","7",".",".",".","."],
["6",".",".","1","9","5",".",".","."],
[".","9","8",".",".",".",".","6","."],
["8",".",".",".","6",".",".",".","3"],
["4",".",".","8",".","3",".",".","1"],
["7",".",".",".","2",".",".",".","6"],
[".","6",".",".",".",".","2","8","."],
[".",".",".","4","1","9",".",".","5"],
[".",".",".",".","8",".",".","7","9"]
]
輸出: false
解釋: 除了第一行的第一個數字從 5 改爲 8 以外,空格內其他數字均與 示例1 相同。
但由於位於左上角的 3x3 宮內有兩個 8 存在, 因此這個數獨是無效的。
說明:
. 一個有效的數獨(部分已被填充)不一定是可解的。
. 只需要根據以上規則,驗證已經填入的數字是否有效即可。
. 給定數獨序列只包含數字 1-9 和字符 '.' 。
. 給定數獨永遠是 9x9 形式的。
思路解析:用的boolean數組,但卻像是map的思想,num作爲每行的元素值,再用3個數組分別行,列與3x3的情況,boolean[9][10]長度爲10因爲不用0,當然這種方式不好想到。下面的暴力破解法寫的比較優雅,也很好理解,k=i+1;總是從下一行開始與當前元素比較
public boolean isValidSudoku(char[][] board) {
// 記錄某行,某位數字是否已經被擺放
boolean[][] row = new boolean[9][10];
// 記錄某列,某位數字是否已經被擺放
boolean[][] col = new boolean[9][10];
// 記錄某 3x3 宮格內,某位數字是否已經被擺放
boolean[][] block = new boolean[9][10];
for (int i = 0; i < 9; i++) {
for (int j = 0; j < 9; j++) {
if (board[i][j] != '.') {
int num = board[i][j] - '0';
if (row[i][num] || col[j][num] || block[i / 3 * 3 + j / 3][num]) {
return false;
} else {
row[i][num] = true;
col[j][num] = true;
block[i / 3 * 3 + j / 3][num] = true;
}
}
}
}
return true;
}
//暴力破解
public static boolean isValidSudoku(char[][] board) {
for(int i = 0; i < 9; i++){
for(int j = 0; j < 9; j++){
if(board[i][j] == '.')continue;
for(int k = 8; k > j; k--){
if(board[i][j] == board[i][k])
return false;
}
for(int k = 8; k > i; k--){
if(board[i][j] == board[k][j])
return false;
}
for(int k = i + 1; k % 3 != 0; k ++){
for(int h = j / 3 * 3;h < j / 3 * 3 + 3; h ++ ){
if(board[i][j] == board[k][h])
return false;
}
}
}
}
return true;
}
10. 給定一個 n × n 的二維矩陣表示一個圖像。將圖像順時針旋轉 90 度。
說明:你必須在原地旋轉圖像,這意味着你需要直接修改輸入的二維矩陣。請不要使用另一個矩陣來旋轉圖像。
給定 matrix =
[
[1,2,3],
[4,5,6],
[7,8,9]
],
原地旋轉輸入矩陣,使其變爲:
[
[7,4,1],
[8,5,2],
[9,6,3]
]
給定 matrix =
[
[ 5, 1, 9,11],
[ 2, 4, 8,10],
[13, 3, 6, 7],
[15,14,12,16]
],
原地旋轉輸入矩陣,使其變爲:
[
[15,13, 2, 5],
[14, 3, 4, 1],
[12, 6, 8, 9],
[16, 7,10,11]
]
思路解析:左下右上連線,元素對換後再鏡像對稱,這種方法化繁爲簡。
public void rotate(int[][] matrix) {
int len=matrix[0].length;
int mid=len/2;
for(int i=1;i<len;i++){
for(int j=0;j<i;j++){
int temp=matrix[i][j];
matrix[i][j]=matrix[j][i];
matrix[j][i]=temp;
}
}
for(int i=0;i<len;i++){
for(int j=0;j<mid;j++){
int temp=matrix[i][j];
matrix[i][j]=matrix[i][len-j-1];
matrix[i][len-j-1]=temp;
}
}
}
//暴力
public void rotate(int[][] matrix) {
int len = matrix.length;
for (int i = 0; i < len / 2; i++) {
int start = i;
int end = len - i - 1;
for (int j = 0; j < end - start; j++) {
int temp = matrix[start][start + j];
matrix[start][start + j] = matrix[end - j][start];
matrix[end - j][start] = matrix[end][end - j];
matrix[end][end - j] = matrix[start + j][end];
matrix[start + j][end] = temp;
}
}
}
11.給定一個包含 n 個整數的數組 nums
,判斷 nums
中是否存在三個元素 a,b,c ,使得 a + b + c = 0 ?找出所有滿足條件且不重複的三元組。
注意:答案中不可以包含重複的三元組。
例如, 給定數組 nums = [-1, 0, 1, 2, -1, -4],
滿足要求的三元組集合爲:
[
[-1, 0, 1],
[-1, -1, 2]
]
思路解析:排序,然後定義前後指針指向i+1與length,i爲遍歷的索引,內層循環當while(l<r)判斷if (nums[l] + nums[r] == sum) ,符合條件的存入,當左邊大時l++,否則r--,可以跳過重複元素。
public List<List<Integer>> threeSum(int[] nums) {
Arrays.sort(nums);
List<List<Integer>> ls = new ArrayList<>();
for (int i = 0; i < nums.length - 2; i++) {
if (i == 0 || (i > 0 && nums[i] != nums[i - 1])) { // 跳過可能重複的答案
int l = i + 1, r = nums.length - 1, sum = 0 - nums[i];
while (l < r) {
if (nums[l] + nums[r] == sum) {
ls.add(Arrays.asList(nums[i], nums[l], nums[r]));
while (l < r && nums[l] == nums[l + 1]) l++;
while (l < r && nums[r] == nums[r - 1]) r--;
l++;
r--;
} else if (nums[l] + nums[r] < sum) {
while (l < r && nums[l] == nums[l + 1]) l++; // 跳過重複值
l++;
} else {
while (l < r && nums[r] == nums[r - 1]) r--;
r--;
}
}
}
}
return ls;
}
12.給定一個 m x n 的矩陣,如果一個元素爲 0,則將其所在行和列的所有元素都設爲 0。請使用原地算法。
輸入:
[
[1,1,1],
[1,0,1],
[1,1,1]
]
輸出:
[
[1,0,1],
[0,0,0],
[1,0,1]
]
進階:
一個直接的解決方案是使用 O(mn) 的額外空間,但這並不是一個好的解決方案。
一個簡單的改進方案是使用 O(m + n) 的額外空間,但這仍然不是最好的解決方案。
你能想出一個常數空間的解決方案嗎?
思路解析:利用一個輔助數組來標記爲0的位置,再根據它來做替換。
public void setZeroes(int[][] matrix) {
int h=matrix.length;
int w=matrix[0].length;
int[][] nub=new int[h][w];
for(int i=0;i<h;i++){
for(int j=0;j<w;j++){
if(matrix[i][j]==0){
nub[i][j]=1;
}
}
}
for(int i=0;i<h;i++){
for(int j=0;j<w;j++){
if(nub[i][j]==1){
change(i,j,w,h,matrix);
}
}
}
}
public void change(int i,int j,int w,int h,int[][] matrix) {
for(int k=0;k<w;k++){
matrix[i][k]=0;
}
for(int l=0;l<h;l++){
matrix[l][j]=0;
}
}
13.給定一個字符串數組,將字母異位詞組合在一起。字母異位詞指字母相同,但排列不同的字符串。
示例:
輸入: ["eat", "tea", "tan", "ate", "nat", "bat"],
輸出:
[
["ate","eat","tea"],
["nat","tan"],
["bat"]
]
說明:
所有輸入均爲小寫字母。
不考慮答案輸出的順序。
思路解析:每次循環取一個字符串,將它變爲字符數組排序,利用map來存入,當然存入前利用containKey()比較是否存在
public List<List<String>> groupAnagrams(String[] strs) {
if (strs.length == 0) return new ArrayList();
Map<String, List> ans = new HashMap<String, List>();
for (String s : strs) {
char[] ca = s.toCharArray();
Arrays.sort(ca);
String key = String.valueOf(ca);
if (!ans.containsKey(key)) ans.put(key, new ArrayList());
ans.get(key).add(s);
}
return new ArrayList(ans.values());
}
14.假設按照升序排序的數組在預先未知的某個點上進行了旋轉。( 例如,數組 [0,1,2,4,5,6,7] 可能變爲 [4,5,6,7,0,1,2] )。
搜索一個給定的目標值,如果數組中存在這個目標值,則返回它的索引,否則返回 -1 。你可以假設數組中不存在重複的元素。
你的算法時間複雜度必須是 O(log n) 級別。
示例 1:
輸入: nums = [4,5,6,7,0,1,2], target = 0
輸出: 4
思路解析:將數組一分爲二,其中一定有一個是有序的,另一個可能是有序,也能是部分有序。此時有序部分用二分法查找。無序部分再一分爲二,其中一個一定有序,另一個可能有序,可能無序。就這樣循環.
public int search(int[] nums, int target) {
return search(nums,target,0,nums.length-1);
}
private int search(int[] nums,int target,int i,int j)
{
if(i>j) return -1;
int mid=i+(j-i)/2;
if(nums[mid]==target) return mid;
if(nums[i]<nums[j]){
if(nums[mid]<target) return search(nums,target,mid+1,j);
else return search(nums,target,i,mid-1);
}
else{
int ans=search(nums,target,i,mid-1);
return ans!=-1?ans:search(nums,target,mid+1,j);
}
}
15.給定一個大小爲 n 的數組,找到其中的衆數。衆數是指在數組中出現次數大於 ⌊ n/2 ⌋
的元素。
你可以假設數組是非空的,並且給定的數組總是存在衆數。