初始設(shè)置
#define OK 1
#define ERROR 0
/* ElemType類型根據(jù)實(shí)際情況而定挑童,這里假設(shè)為int */
typedef int ElemType;
/* Status是函數(shù)的類型,其值是函數(shù)結(jié)果狀態(tài)代碼累铅,如OK等 */
typedef int Status;
// 鏈表結(jié)構(gòu)設(shè)計(jì)
typedef struct Node {
ElemType data;
struct Node *next;
} Node, *LinkList;
// 初始化
Status ListInit(LinkList *L, ElemType array[], int count)
{
LinkList tail = (LinkList)malloc(sizeof(Node));
if (!tail) return ERROR;
*L = tail;
for (int i = 0; i < count; i++) {
tail->next = (LinkList)malloc(sizeof(Node));
if (!tail->next) return ERROR;
tail->next->data = array[i];
tail = tail->next;
}
tail->next = NULL;
return OK;
}
// 遍歷
void PrintList(LinkList L)
{
LinkList tail = L->next;
while (tail) {
printf("%3d", tail->data);
tail = tail->next;
}
printf("\n");
}
1. 題目1
將2個(gè)遞增的有序鏈表合并為?個(gè)鏈表的有序鏈表。
要求:
- 結(jié)果鏈表仍然使?兩個(gè)鏈表的存儲(chǔ)空間站叼,不另外占?其他的存儲(chǔ)空間娃兽。
- 表中不不允許有重復(fù)的數(shù)據(jù)。
解答
因?yàn)椴荒苷加眯碌目臻g尽楔,即空間復(fù)雜度是投储。
我們只需要使用某一個(gè)鏈表頭作為輸出結(jié)果的表頭,這里我使用L1阔馋。
想象條根鏈表玛荞,通過(guò)一根針串起來(lái)。L1和L2的遍歷指針呕寝,誰(shuí)小就移動(dòng)誰(shuí)勋眯,相等時(shí)同時(shí)移動(dòng)。比如下圖示意3<4下梢,p1移動(dòng)到5客蹋。
需要注意的是,L2中重復(fù)的節(jié)點(diǎn)需要釋放孽江,否則會(huì)內(nèi)存泄露讶坯。
時(shí)間復(fù)雜度:
空間復(fù)雜度:
Status MergeTwoLists(LinkList *L1, LinkList *L2, LinkList *L3)
{
// 另一個(gè)表為空表
if (L1 == NULL || !(*L1)->next) {
*L3 = *L2;
return OK;
} else if (L2 == NULL || !(*L2)->next) {
*L3 = *L1;
return OK;
}
// 初始化
LinkList p1, p2, pre, tmp;
p1 = (*L1)->next;
p2 = (*L2)->next;
*L3 = pre = *L1; // 確定表頭為L(zhǎng)1
while (p1 && p2) {
if (p1->data < p2->data) { // <
pre->next = p1;
pre = p1;
p1 = p1->next;
} else if (p1->data > p2->data) { // >
pre->next = p2;
pre = p2;
p2 = p2->next;
} else { // ==
pre->next = p1;
pre = p1;
p1 = p1->next;
tmp = p2; // 準(zhǔn)備釋放相同的數(shù)
p2 = p2->next;
free(tmp);
}
}
pre->next = p1 ? p1 : p2;
return OK;
}
int main(int argc, const char * argv[]) {
LinkList L1, L2, L3;
ElemType a[6] = {1, 3, 5, 6, 7, 9};
ElemType b[6] = {2, 4, 5, 6, 8, 10};
ListInit(&L1, a, 6);
ListInit(&L2, b, 6);
MergeTwoLists(&L1, &L2, &L3);
PrintList(L3);
return 0;
}
// 輸出
1 2 3 4 5 6 7 8 9 10
2. 題目2
已知兩個(gè)鏈表A和B分別表示兩個(gè)集合,其元素遞增排列竟坛。設(shè)計(jì)?個(gè)算法闽巩,用于求出A與B的交集,并存儲(chǔ)在A鏈表中担汤。
例如 : La = {2,4,6,8}; Lb = {4,6,8,10}; 輸出La = {4,6,8}涎跨。
解答
思路和題目1類似,要求結(jié)果存放于L1崭歧,那么我們就選L1作為輸出結(jié)果的表頭隅很。
同樣是雙指針遍歷,誰(shuí)小就移動(dòng)誰(shuí)率碾,相等時(shí)同時(shí)移動(dòng)叔营。但這一次需要釋放L1中不用的節(jié)點(diǎn)。
先釋放比L2中小的部分所宰,取交集之后绒尊,如果p1還沒(méi)有到頭,說(shuō)明還有更大的元素仔粥,需要釋放這部分婴谱。最后尾節(jié)點(diǎn)置空蟹但。
時(shí)間復(fù)雜度:
空間復(fù)雜度:
Status intersection(LinkList *L1, LinkList *L2, LinkList *L3)
{
if (L1 == NULL || L2 == NULL) return ERROR;
// 初始化
LinkList p1, p2, pre, tmp;
p1 = (*L1)->next;
p2 = (*L2)->next;
*L3 = pre = *L1; // 確定表頭為L(zhǎng)1
while (p1 && p2) {
if (p1->data < p2->data) { // <
tmp = p1; // 釋放小于部分
p1 = p1->next;
free(tmp);
} else if (p1->data > p2->data) { // >
p2 = p2->next;
} else { // ==
pre->next = p1;
pre = p1;
p1 = p1->next;
p2 = p2->next;
}
}
while (p1) { // 釋放大于部分
tmp = p1;
p1 = p1->next;
free(tmp);
}
pre->next = NULL;
return OK;
}
int main(int argc, const char * argv[]) {
LinkList L1, L2, L3;
ElemType a[4] = {2, 4, 6, 8};
ElemType b[4] = {4, 6, 8, 10};
ListInit(&L1, a, 4);
ListInit(&L2, b, 4);
intersection(&L1, &L2, &L3);
PrintList(L3);
return 0;
}
// 輸出
4 6 8
3. 題目3
設(shè)計(jì)?個(gè)算法,將鏈表中所有節(jié)點(diǎn)的鏈接方向"原地旋轉(zhuǎn)"谭羔。
- 僅利用原表的存儲(chǔ)空間华糖,即要求算法空間復(fù)雜度為O(1)。
- 例如:L={0,2,4,6,8,10}瘟裸,逆轉(zhuǎn)后:L = {10,8,6,4,2,0}客叉。
解答
單鏈表的逆序。需要前一個(gè)節(jié)點(diǎn)和后一個(gè)節(jié)點(diǎn)的指針话告,方便指針逆向兼搏,和繼續(xù)遍歷。
時(shí)間復(fù)雜度:
空間復(fù)雜度:
Status reverse(LinkList *L1)
{
if (*L1 == NULL) return ERROR;
LinkList pre = NULL, cur = *L1, next = (*L1)->next;
while (next) {
cur = next;
next = next->next;
cur->next = pre;
pre = cur;
}
(*L1)->next = cur;
return OK;
}
int main(int argc, const char * argv[]) {
LinkList L1;
ElemType a[6] = {0, 2, 4, 6, 8, 10};
ListInit(&L1, a, 6);
reverse(&L1);
PrintList(L1);
return 0;
}
//輸出
10 8 6 4 2 0
4. 題目4
設(shè)計(jì)?個(gè)算法超棺,刪除遞增有序鏈表中值大于等于mink且?于等于maxk(mink向族、maxk是給定的兩個(gè)參數(shù),值可以和表中的元素相同棠绘,也可以不同)的所有元素件相。
解答
單向鏈表節(jié)點(diǎn)的刪除。不知道是否還有更好的方法氧苍。
時(shí)間復(fù)雜度:
空間復(fù)雜度:
Status delete(LinkList *L1, ElemType mink, ElemType maxk)
{
if (*L1 == NULL) return ERROR;
LinkList pre = *L1, cur = (*L1)->next, tmp;
while (cur) {
if (mink <= cur->data && cur->data <= maxk) {
tmp = cur;
pre->next = cur->next;
cur = cur->next;
free(tmp);
} else {
pre = cur;
cur = cur->next;
}
}
return OK;
}
int main(int argc, const char * argv[]) {
LinkList L1;
ElemType a[6] = {0, 2, 4, 6, 8, 10};
ListInit(&L1, a, 6);
delete(&L1, 1, 7);
PrintList(L1);
return 0;
}
// 輸出
0 8 10
5. 題目5
設(shè)將n(n>1)個(gè)整數(shù)存放到?維數(shù)組R中夜矗,試設(shè)計(jì)?個(gè)在時(shí)間和空間兩?面都盡可能高效的算法。將R中保存的序列循環(huán)左移p個(gè)位置(0<p<n)個(gè)位置让虐,即將R中的數(shù)據(jù)由(x0,x1,......,xn-1)變換為 (xp,xp+1,...,xn-1,x0,x1,...,xp-1)紊撕。
例如:pre[10] = {0,1,2,3,4,5,6,7,8,9}, n = 10,p = 3; pre[10] = {3,4,5,6,7,8,9,0,1,2}
解答
這里直接思路是:
- 每次取頭數(shù)字
- 后面的元素前移1格
- 把最后一個(gè)改為頭數(shù)字
這個(gè)時(shí)間復(fù)雜度是
。
我們注意到先逆序{9,8,7,6,5,4,3,2,1,0}赡突,然后把3-9和0-2再分別逆序就可以了对扶。
時(shí)間復(fù)雜度:
空間復(fù)雜度:
void Reverse(ElemType a[], int left, int right)
{
if (left > right) return;
int mid = (left + right) / 2;
ElemType tmp;
for (int i = 0; i <= mid - left; i++){
tmp = a[left + i];
a[left + i] = a[right - i];
a[right - i] = tmp;
}
}
void MoveLeft(ElemType a[], int n, int p)
{
Reverse(a, 0, n-1);
Reverse(a, 0, n-1-p);
Reverse(a, n-p, n-1);
}
int main(int argc, const char * argv[]) {
int n = 10, p = 3;
ElemType *array = (ElemType *)malloc(sizeof(ElemType) * n);
for (int i = 0; i < n; i++) {
array[i] = i;
}
MoveLeft(array, n, p);
for (int i = 0; i < n; i++) {
printf("%d ", array[i]);
}
printf("\n");
free(array);
return 0;
}
// 輸出
3 4 5 6 7 8 9 0 1 2
6. 題目6
已知?個(gè)整數(shù)序列列A = (a0,a1,a2,...an-1),其中0<=ai<=n, 0<=i<=n惭缰。 若存在ap1= ap2 = ...=apm = x浪南,且m>n/2, 0<=pk<n,1<=k<=m,則稱x為A的主元素漱受。
例如络凿,A=(0,5,5,3,5,7,5,5),則5是主元素; 若B=(0,5,5,3,5,1,5,7)昂羡,則B中沒(méi)有主元素絮记。
假設(shè)A中的n個(gè)元素保存在?個(gè)一維數(shù)組中,請(qǐng)?jiān)O(shè)計(jì)?個(gè)盡可能?效的算法虐先,找出數(shù)組元素中的主元素怨愤,若存在主元素則輸出該元素,否則輸出-1蛹批。
解答
注意到主元素滿足數(shù)量大于總數(shù)的一半憔四。那么主元素?cái)?shù)量減去其他元素?cái)?shù)量肯定是大于0的膀息。
也可以理解為,主元素和其他元素依次抵消了赵,剩下的肯定是主元素。
利用這個(gè)思路甸赃,從第一個(gè)元素開(kāi)始作為主元素柿汛,下一個(gè)元素若和它相等,計(jì)數(shù)加一埠对,否則減一络断。當(dāng)計(jì)數(shù)為0的時(shí)候,將主元素替換為下一個(gè)元素项玛。
時(shí)間復(fù)雜度:
空間復(fù)雜度:
#define NOT_FOUND -1
int FindMainElement(ElemType *a, int count)
{
if (a == NULL || count == 0) return NOT_FOUND;
// 統(tǒng)計(jì)出現(xiàn)較多的數(shù)
int statistics = 1;
ElemType mainElem = a[0];
for (int i = 1; i < count; i++) {
if (a[i] == mainElem) {
++statistics;
} else {
--statistics;
}
if (statistics == 0) {
statistics = 1;
mainElem = a[i];
}
}
// 出現(xiàn)較多貌笨,但不一定滿足主元素?cái)?shù)量>n/2的條件
if (statistics > 0) {
statistics = 0;
for (int i = 1; i < count; i++)
if (a[i] == mainElem) ++statistics;
if (statistics > count / 2)
return mainElem;
}
return NOT_FOUND;
}
int main(int argc, const char * argv[]) {
ElemType a[8] = {0,5,5,3,5,7,5,5};
int mainElem = FindMainElement(a, 8);
if (mainElem > 0) {
printf("The main element is %d.\n", mainElem);
} else {
printf("The main element is not found.\n");
}
return 0;
}
// 輸出
The main element is 5.
7. 題目7
?單鏈表保存m個(gè)整數(shù),結(jié)點(diǎn)的結(jié)構(gòu)為(data, link)襟沮, 且|data|<=n(n為正整數(shù))∽锻铮現(xiàn)在要去設(shè)計(jì)一個(gè)時(shí)間復(fù)雜度盡可能高效的算法。對(duì)于鏈表中的data絕對(duì)值相等的結(jié)點(diǎn)开伏,僅保留第?次出現(xiàn)的結(jié)點(diǎn)膀跌,刪除其余絕對(duì)值相等的結(jié)點(diǎn)。
例如固灵,鏈表A = {21,-15,15,-7,15}捅伤,刪除后的鏈表A={21,-15,-7}。
解答
這個(gè)只要求時(shí)間復(fù)雜度盡可能高效巫玻,潛臺(tái)詞就是說(shuō)丛忆,我們可以拿空間換時(shí)間。
我們用一個(gè)char表來(lái)記錄某個(gè)元素是否出現(xiàn)過(guò)仍秤,如果出現(xiàn)過(guò)為1熄诡,否則為0。再次出現(xiàn)時(shí)徒扶,刪除對(duì)應(yīng)節(jié)點(diǎn)粮彤。
又由于條件,那么表的大小為
姜骡。
時(shí)間復(fù)雜度:
空間復(fù)雜度:
Status RemoveAbsRepeat(LinkList *L, int n)
{
char *flag = (char *)calloc(n + 1, sizeof(char));
if (flag == NULL) return ERROR;
LinkList pre, cur, tmp;
pre = *L;
cur = pre->next;
while (cur) {
int value = cur->data > 0 ? cur->data : cur->data * -1;
if (flag[value - 1]) {
tmp = cur;
cur = cur->next;
pre->next = cur;
free(tmp);
} else {
flag[value - 1] = 1;
pre = cur;
cur = cur->next;
}
}
free(flag);
return OK;
}
int main(int argc, const char * argv[]) {
LinkList L1;
ElemType a[6] = {0, 15, 3, -3, -15, 2};
ListInit(&L1, a, 6);
RemoveAbsRepeat(&L1, 16);
PrintList(L1);
return 0;
}
// 輸出
0 15 3 2