一、數組的問題
給定一個數組 nums,編寫一個函數將所有 0 移動到數組的末尾,同時保持非零元素的相對順序。
示例:
輸入: [0,1,0,3,12]
輸出: [1,3,12,0,0]
說明:
必須在原數組上操作,不能拷貝額外的數組。
儘量減少操作次數。
//時間複雜度O(n),空間複雜度O(n)
class Solution {
public void moveZeroes(int[] nums) {
List<Integer> nonZeroList=new ArrayList<>();;
for(int i=0;i<nums.length;i++){
if(nums[i]!=0){
nonZeroList.add(nums[i]);
}
}
for(int i=0;i<nonZeroList.size();i++){
nums[i]=nonZeroList.get(i);
}
for(int i=nonZeroList.size();i<nums.length;i++){
nums[i]=0;
}
}
}
//空間複雜度O(1)
public void moveZeroes(int[] nums) {
int k = 0;//nums中,[0...k)的元素均爲非0元素
//遍歷到第i個元素後,保證[0...i]中所有非0元素
//都按照順序排列在[0...k)中
for (int i = 0; i < nums.length; i++) {
if (nums[i] != 0) {
nums[k++] = nums[i];
}
}
//將剩餘位置置爲0
for (int i = k; i < nums.length; i++) {
nums[i] = 0;
}
}
相關題目26,27,80
二、基礎算法思路的應用
給定一個有n個元素的數組,數組中元素的取值只有0,1,2三種可能,爲這個數組排序
public void sortColors(int[] nums) {
//計數排序:掃描一遍數組,計算出0,1,2有多少個,將他們在分別放回
//計數排序適用於元素個數有限的情況
//時間複雜度O(n)
//空間複雜度O(k),k爲元素的取值範圍
int count[] = new int[3];//存放0,1,2三個元素的頻率
for (int i = 0; i < nums.length; i++) {
//assert(nums[i]>=0&&nums[i]<=2);
count[nums[i]]++;
}
int index = 0;
for (int i = 0; i < count[0]; i++) {
nums[index++] = 0;
}
for (int i = 0; i < count[1]; i++) {
nums[index++] = 1;
}
for (int i = 0; i < count[2]; i++) {
nums[index++] = 2;
}
}
使用一遍遍歷完成操作
三路快速排序思路
每次選取一個標定點但是由於這個標定點有很多個和這個元素相等的值,所以將整個數組分成三份,<v, =v, >v,之後遞歸的對<v和>v的地方進行三路快排。
這道題目是由3個元素,只需要對整個數組進行一次三路快排
int zero = -1;//nums[0...zero]==0;
int two = nums.length;//nums[teo...n-1]==2
for (int i = 0; i < two; ) {
if (nums[i] == 1) {
i++;
} else if (nums[i] == 2) {
two--;
int temp = nums[i];
nums[i] = nums[two];
nums[two] = temp;
} else { //nums[i]==0
zero++;
int temp = nums[zero];
nums[zero] = nums[i];
nums[i] = temp;
i++;
}
}
相關問題88,215
給定一個升序排列整型數組和一個數組target,在其中尋找2個元素,使其和爲target,返回這兩個數的索引。
如numbers=[2,7,11,15],target=9
返回數字2,7的索引1,2(索引從1開始計算)
- 思路1,暴力解法,雙重循環,但是這樣會O(n^2)超時
-
思路2,暴力解法沒有充分利用原數組的性質——有序,通過有序可以聯想到二分搜索。依次遍歷每個數據nums[i]對於每個nums[i]在剩餘的數組中尋找target-nums[i],每次遍歷都是進行二分查找複雜度O(longn),所以這個思路的複雜度是O(nlogn)
-
思路3,由於我們要尋找到2個索引,他們對應的數字和爲target,這兩個索引子數組中一定是一左一右,所以在初始化的時候選擇最左側的數字i,最右側的數字j,在初始的情況下看nums[i]+nums[j]是否==target,如果是就找到了答案。如果是nums[i]+nums[j]<target,此時將i++(因爲數組是有序的,此時的nums[i]一定比前一個多一些),此時在判斷是否等於target,如果此時nums[i]+nums[j]>target,那麼就要j--(此時的nums[j]一定比前一個小一些)。複雜度O(n)
public int[] twoSum(int[] numbers, int target) {
int l = 0, r = numbers.length - 1;
while (l < r) {
if (numbers[l] + numbers[r] == target) {
int res[] = {l + 1, r + 1};
return res;
} else if (numbers[l] + numbers[r] < target) {
l++;
} else if (numbers[l] + numbers[r] > target) {
r--;
}
}
throw new IllegalArgumentException();
}
相關問題125,344,345,11
三、滑動窗口
給定一個含有 n 個正整數的數組和一個正整數 s ,找出該數組中滿足其和 ≥ s 的長度最小的連續子數組。如果不存在符合條件的連續子數組,返回 0。
如s = 7, nums = [2,3,1,2,4,3],輸出: 2
- 暴力解法:遍歷所有連續子數組[i...j],計算其和sum,驗證sum>=s,時間複雜度O(n^3)
- 暴力解法的問題是包含大量的重複計算,當看到[i...j]這個連續子數組,如果這個和不到s,那麼向後看一個數據,將他納入到這個連續子數組中,[i...j+1],以此類推,直到某個時刻和大於s了就找到了那組條件的子數組,將他的長度記錄下來,然後從i這邊縮小數組,i--,在不斷的縮小i的過程中,某個時刻和就小於s了,此時j繼續向後找到一個連續子數組讓和大於s,以此類推
import static java.lang.Math.min;
class Solution {
public int minSubArrayLen(int s, int[] nums) {
int l = 0, r = -1;//nums[l...r]爲滑動窗口
int sum = 0;
int res = nums.length + 1;
while (l < nums.length) {
if (r + 1 < nums.length && sum < s) {
sum += nums[++r];
} else {
sum -= nums[l++];
}
if (sum >= s) {
res = min(res, r - l + 1);
}
}
if (res == nums.length + 1) {
return 0;
}
return res;
}
}
給定一個字符串,請你找出其中不含有重複字符的 最長子串 的長度。
- 依然使用滑動窗口來解決問題
import static java.lang.Math.max;
class Solution {
public int lengthOfLongestSubstring(String s) {
char[] c = s.toCharArray();
int[] freq = new int[256];//開始時所有子數組元素出現的頻率爲0
int l = 0, r = -1;
int res = 0;
while (l < c.length) {
if (r + 1 < c.length && freq[c[r + 1]] == 0) {
freq[c[++r]]++;
} else {
freq[c[l++]]--;
}
res = max(res, r - l + 1);
}
return res;
}
}
相關問題,438,76