指針首尾並進
- 快排分割數組首尾的實現方式。
- 輸入一個整數數組,調整數組中數字的順序,使得所有奇數位於數組的前半部分,所有偶數位於數組的後半部分。要求時間複雜度爲O(n)。
- 輸入一個增序數組和一個數sum,在數組中找到兩個數,使得和爲sum。輸入任意一對即可。
1、思路:
先隨機選base,與尾交換。然後從左往右遍歷,找到比base大的數,交換;從右往左遍歷,找到比base小的數,交換。
#include <iostream>
#include <exception>
#include <stdlib.h>
#include <string.h>
using namespace std;
int RandInRange(int a, int b) {
return rand()%(b - a + 1) + a;
}
void PrintArray(int data[], int length) {
for (int i = 0; i < length; i++)
printf("%d ", data[i]);
printf("\n");
}
int Partition(int data[], int length, int start, int end) {
if (data == NULL || length <= 0 || start < 0 || end >= length)
throw new exception();
int index = RandInRange(start, end);
int base = data[index];
swap(data[start], data[index]);
while(start < end) {
while (start < end && data[end] > base)
end--;
data[start] = data[end];
while (start < end && data[start] < base)
start++;
data[end] = data[start];
}
data[start] = base;
return start;
}
void QuickSort(int data[], int length, int start, int end) {
if (start == end)
return;
int index = Partition(data, length, start, end);
if (index > start)
QuickSort(data, length, start, index - 1);
if (index < end)
QuickSort(data, length, start + 1, end);
}
int main() {
int test[7] = {23, 13, 49, 6, 31, 19, 28};
PrintArray(test, 7);
QuickSort(test, 7, 0, 6);
PrintArray(test, 7); 60
}
2、思路:
設置兩個指針,首尾位置。首指針往後移直到遇到偶數,尾指針往前移直到遇到奇數,兩者都遇到的情況下互換位置,終止條件是首尾指針碰頭。
void ReorderOddEven(int *pData, unsigned int length) {
if(pData == NULL || length == 0)
return;
int *pBegin = pData;
int *pEnd = pData + length - 1;
while(pBegin < pEnd) {
// 向後移動pBegin,直到它指向偶數
while(pBegin < pEnd && (*pBegin & 0x1) != 0)
pBegin ++;
// 向前移動pEnd,直到它指向奇數
while(pBegin < pEnd && (*pEnd & 0x1) == 0)
pEnd --;
if(pBegin < pEnd) {
int temp = *pBegin;
*pBegin = *pEnd;
*pEnd = temp;
}
}
}
3、思路:
如果原數組是無序的,則先排序,O(nlogn)開銷。這裏已經是增序排列了,所以不需要排序。設置兩個指針,一頭一尾。當和大於s,尾指針往前移;當和小於s,頭指針往後移。直到兩指針相遇,看是否有符合要求的數出現。
FindNumbersWithSum
bool FindNumbersWithSum(int data[], int length, int sum, int *num1, int *num2) {
bool found = false;
if (length < 1 || num1 == NULL || num2 == NULL)
return found;
int ahead = length - 1;
int behind = 0;
while (ahead > behind) {
long long curSum = data[ahead] + data[behind];
if (curSum == sum) {
*num1 = data[behind];
*num2 = data[ahead];
found = true;
break;
} else if (curSum > sum)
ahead--;
else
behind++;
}
return found;
}
最後總結
- 能用首尾指針技巧解決的問題,往往序列是存在規律的,但其實這種要求並不嚴格
- 但是要求首尾指針根據制定的規則往中間移動的過程中,一定不會錯過答案
- 制定的規則與數據狀況和問題本身相關
左程雲左神:算法面試中的首尾指針技巧
ps:不瞭解左神的朋友可以去百度一下
1:介紹首尾指針技巧
2:詳解兩道利用首尾指針技巧解決的題
左程雲左神的《程序員代碼面試指南 IT名企算法與數據結構題目最優解》
目錄(算法有分 將、校、尉、士四個等級來表示難易程度)
第1章棧和隊列
設計一個有getMin功能的棧(士★)
由兩個棧組成的隊列(尉★★)
如何僅用遞歸函數和棧操作逆序一個棧(尉★★)
貓狗隊列(士★)
用一個棧實現另一個棧的排序(士★)
用棧來求解漢諾塔問題(校★★★)
生成窗口最大值數組(尉★★)
構造數組的MaxTree (校★★★)
求最大子矩陣的大小(校★★★)
最大值減去最小值小於或等於num的子數組數量(校★★★)
限於篇幅原因,同時也爲了大家更好的閱讀,只截取了部分目錄,感興趣的朋友可以幫忙轉發文章後,關注私信回覆【學習】來免費獲取
第1章棧和隊列
設計一個有getMin功能的棧(士★)