題目:
將字串 "PAYPALISHIRING" 以Z字形排列成給定的行數:
P A H N
A P L S I I G
Y I R
複製程式碼
之後從左往右,逐行讀取字元:"PAHNAPLSIIGYIR"
實現一個將字串進行指定行數變換的函式:
string convert(string s, int numRows); 示例 1:
輸入: s = "PAYPALISHIRING", numRows = 3 輸出: "PAHNAPLSIIGYIR" 示例 2:
輸入: s = "PAYPALISHIRING", numRows = 4 輸出: "PINALSIGYAHRPI" 解釋:
P I N
A L S I G
Y A H R
P I
複製程式碼
思路1:按行排序
-
題目要求有兩個,一個是字元是按Z字形排序,一個由上而下按行輸出。
-
分析下Z字形的排序規律。z字形先是由上而下,到了底部再向上,每到底部或者頂部就會反彈,程式中使用bool變數lineUp記錄方向。遍歷字元的同時,使用一個二維陣列int a[row][strlen(s)]來儲存行與元素原始下標的關係。遍歷完畢後,按行拼接字串並輸出。
-
元素的下標與輸出後所在的行有這樣的對應關係。
#row #數字對應元素下標。 0 0P 6I 12N 1 1A 5L 7S 11I 13G 2 2Y 4A 8H 10R 3 3P 9I 複製程式碼
-
程式碼
char* convert(char* s, int numRows) { if (numRows == 1) { return s; } bool lineUp = true; int len = strlen(s); //記錄字元的二維陣列。其實可以使用int的二維陣列,因為字元能自動轉成asscii對應的int數字。 char **pArr = (char**)malloc(sizeof(char*) * numRows); //這個陣列是為了記錄當前行最大下標,用於後面遍歷。其實可以不用,在記錄字串的時候末尾新增'\0'用於判斷就行。 int *indexArr = (int*)malloc(sizeof(int) * numRows); memset(indexArr, -1, sizeof(int) * numRows); for (int i = 0; i < numRows; i++ ) { //開始的時候設定長度為len 。在leetcode上執行報超出記憶體限制。分析後改為len /2 + 1,因為兩行的時候最多為len / 2 + 1;超過三行字元被分散到其他行行,也不會超過len /2 + 1。修改後執行通過。 char *a = (char*)malloc(sizeof(char*) * (len / 2 + 1)); memset(a, '#', sizeof(char*) * (len / 2 + 1)); pArr[i] = a; } int j = 0; for (int i = 0; i < len; i ++) { int currentIndex = indexArr[j] + 1; indexArr[j] = currentIndex; pArr[j][currentIndex] = s[i]; if (lineUp) { if (j == numRows - 1) { j --; lineUp = false; } else { j ++; } } else { if (j == 0) { j ++; lineUp = true; } else { j --; } } } char *p = (char*)malloc(sizeof(char*) * len); memset(p, 0, sizeof(char*) * len); int jj = 0; for (int ii = 0; ii < numRows ; ii ++){ char *temp = pArr[ii]; for (int k = 0; k <= indexArr[ii]; k++) { p[jj++] = temp[k]; } } free(pArr); free(indexArr); return p; } 複製程式碼
-
複雜度分析
- 時間複雜度 O(n), n = strlen(s);
- 空間複雜度 O(n)。
思路2:按行訪問
-
這個是對字元輸入的方式分析後得出的數學規律。首尾兩行成等比關係,中間的行除了等比的位置填充元素外,另外還需要在等比距離倒退一定長度offset的位置填充,offset = currentRow。
-
程式碼
``` char* convert(char* s, int numRows) { int len = strlen(s); //特殊情況特殊處理 if (len == 0 || numRows == 0 || numRows == 1 || numRows == len) { return s; } char *p = (char*)malloc(sizeof(char) * len); //等比間隔 int cycleLen = 2 * numRows - 2; int pIndex = 0; for (int i = 0 ; i < numRows; i ++) { for (int j = 0; j + i < len ; j += cycleLen) { //獲取等比位置的字元 p[pIndex ++] = s[i + j]; //獲取中間行非等比位置的字元 if (i != 0 && i != numRows - 1 && j + cycleLen - i < len) { p[pIndex ++] = s[ j + cycleLen - i]; } } } return p; } ``` 複製程式碼
-
複雜度分析
- 時間複雜度 O(n), n = strlen(s);
- 空間複雜度 O(n)。如果返回字元不視為額外空間,那麼空間複雜度為O(1)。
小結:
- 這道題開始也是用分析規律的方法去做,但舉的例子少了點,寫出來執行才發現規律是錯的。後面也就沒繼續找數學規律。 為了避免下次犯錯,找規律需要論證不少於3個例子。同時規律需要分情況討論,例如該題的首尾兩行作為一類,中間行作為一類。另外別想著用簡單的規律就能處理全部問題,一般這類問題都是二級或者多級的,例如多種情況對應不同的規律,或者同一個規律要處理的多個問題。還有如果一類問題按,一般會存在規律。
- 注意的地方:二維陣列初始化問題;meset的使用;字串、ASCII碼、int之間的關係等。
- 部分程式碼可以寫的更簡潔,但為了更好的描述過程,暫時還是保持這個風格,有利於學習和理解。
待完善L('ω')┘三└('ω')」....