學習筆記:陣列篇
學習筆記:陣列篇
1.基本性質
陣列是一個比較簡單的資料結構,陣列的具體形式是一連串連續地址的儲存,並且其下標是連續的。
0 |
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
a |
d |
g |
G |
k |
l |
h |
d |
e |
b |
10002 |
10003 |
10004 |
10005 |
10006 |
10007 |
10008 |
10009 |
10010 |
10011 |
陣列特點:
下標從零開始
地址連續。
地址連續意味著其實對於陣列來說插入和刪除從一般意義來說會比較麻煩
比如刪除k,其後所有元素的下標和地址會相應移動
所以實際上陣列元素只能覆蓋,不方便刪除。
0 |
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
|
a |
d |
g |
G |
l |
h |
d |
e |
b |
|
10002 |
10003 |
10004 |
10005 |
10006 |
10007 |
10008 |
10009 |
10010 |
2.陣列常用思路
0)暴力遍歷法
1)二分法
2)雙指標
3)思路模擬
其中感覺二分法和雙指標在後面章節都有更為複雜的應用,但是在陣列中的使用卻顯得尤為簡單,體現了其基本的作用方式。
1) 二分法
class Solution {
public:
int search(vector<int>& nums, int target) {
int left = 0;
int right = nums.size() - 1; // 定義target在左閉右閉的區間裡,[left, right]
while (left <= right) { // 當left==right,區間[left, right]依然有效,所以用 <=
int middle = left + ((right - left) / 2);// 防止溢位 等同於(left + right)/2
if (nums[middle] > target) {
right = middle - 1; // target 在左區間,所以[left, middle - 1]
} else if (nums[middle] < target) {
left = middle + 1; // target 在右區間,所以[middle + 1, right]
} else { // nums[middle] == target
return middle; // 陣列中找到目標值,直接返回下標
}
}
// 未找到目標值
return -1;
}
};
二分法及其容易出錯,關鍵就在於隨著迴圈的進行所伴隨的一系列包括溢位,死迴圈等問題。
要解決主要就是要保證每一次迴圈都和第一次一樣,以上程式碼的關鍵處都是保證了每次迴圈的初始條件不會出問題。
2) 雙指標法
從這裡我覺得很有意思,這是不是意味著所有人想象到的不論什麼方法都可以通過程式設計實現去提高效率呢?
這個方法主要是通過兩個指標的變化來達到掃描的目的。兩個指標可以開始在同一側(滑動視窗法),也可以在兩側。
class Solution {
public:
int removeElement(vector<int>& nums, int val) {
int size = nums.size();
for (int i = 0; i < size; i++) {
if (nums[i] == val) { // 發現需要移除的元素,就將陣列集體向前移動一位
for (int j = i + 1; j < size; j++) {
nums[j - 1] = nums[j];
}
i--; // 因為下標i以後的數值都向前移動了一位,所以i也向前移動一位
size--; // 此時陣列的大小-1
}
}
return size;
}
};
3) 模擬行為
基於陣列結構的相對好掌控,所以陣列也被用來實現一些行為思路的實現。例子就是螺旋矩陣的填寫。我開始一直想用矩陣變換去求每個公式,但是還是太不現實了。可以模擬人去填寫陣列的過程。
其中比較大的啟發和教訓就是對於迴圈,可能需要設定很多條件去保證迴圈的某種不變。
class Solution {
public:
vector<vector<int>> generateMatrix(int n) {
vector<vector<int>> res(n, vector<int>(n, 0)); // 使用vector定義一個二維陣列
int startx = 0, starty = 0; // 定義每迴圈一個圈的起始位置
int loop = n / 2; // 每個圈迴圈幾次,例如n為奇數3,那麼loop = 1 只是迴圈一圈,矩陣中間的值需要單獨處理
int mid = n / 2; // 矩陣中間的位置,例如:n為3, 中間的位置就是(1,1),n為5,中間位置為(2, 2)
int count = 1; // 用來給矩陣中每一個空格賦值
int offset = 1; // 每一圈迴圈,需要控制每一條邊遍歷的長度
int i,j;
while (loop --) {
i = startx;
j = starty;
// 下面開始的四個for就是模擬轉了一圈
// 模擬填充上行從左到右(左閉右開)
for (j = starty; j < starty + n - offset; j++) {
res[startx][j] = count++;
}
// 模擬填充右列從上到下(左閉右開)
for (i = startx; i < startx + n - offset; i++) {
res[i][j] = count++;
}
// 模擬填充下行從右到左(左閉右開)
for (; j > starty; j--) {
res[i][j] = count++;
}
// 模擬填充左列從下到上(左閉右開)
for (; i > startx; i--) {
res[i][j] = count++;
}
// 第二圈開始的時候,起始位置要各自加1, 例如:第一圈起始位置是(0, 0),第二圈起始位置是(1, 1)
startx++;
starty++;
// offset 控制每一圈裡每一條邊遍歷的長度
offset += 2;
}
// 如果n為奇數的話,需要單獨給矩陣最中間的位置賦值
if (n % 2) {
res[mid][mid] = count;
}
return res;
}
};
寫在最後:回想之前小學期其實也不是全無收穫,但是奈何學校的小學期真的很難,自己當時也無心學習導致當時信心全無,很長一段時間都在逃避現實,厭惡程式設計。現在來補功課,害,還得慢慢來吧。通過這一系列學習希望能找到自己習的節奏和學習本身的快樂。加油!
(程式碼和思想方法皆轉載自“程式碼隨想錄“,侵刪)”