C語言合併兩個帶頭節點升序排列連結串列
合併連結串列,顧名思義,就是將兩個按順序存放資料的連結串列中的資料合併為用一個連結串列儲存,比如在處理多項式的加減法時就需要將兩個多項式的資料進行合併。合併方式有很多種:如果按照儲存方式的不同,可以將兩個連結串列的資料分別提取出來生成一個新的連結串列來儲存原先兩個連結串列的資料,還可以將其中一個連結串列的資料依次插入到另外一個連結串列的相應位置當中去。在遇到相同資料時可以採取只留下一個數據的方式和兩個資料均保留的方式。這些不同點需要到具體的問題中具體分析,但是隻是在細節上有一些差別,大體的思路都是一樣的,本文主要介紹將一個連結串列插入到另一個連結串列的相應位置,插入完成後銷燬連結串列二,且遇到相同資料採用均保留的方式。
我們還是先拋開連結串列的操作本身,先對兩組不同的資料進行合併操作,知道兩組按升序排列的資料該如何合併成一組資料,我們來分析這樣幾組資料(將第二組的資料插入到第一組中):
1 5 9 13 15
2 7 12
對於這組資料我們可以很輕易的說出應該把“2”插入到“1”的前面,應該把“7”插入到“5”的前面... ... 那麼我們又是如何得到這個結論的呢,接下來我們來用語言細緻的描述一下我們剛才是如何得到這樣的結論的:“首先,遍歷第一組資料,找到第一個比要插入的資料大的資料的前一個數據“i”,再將要插入的資料插入到“i”的後面”。
上述描述解決了插入資料的一般性情況,包括我們用“比該資料大”這樣的話說明了遇到相同資料時該如何處理,但是,問題依然存在,我們來觀察下面一組資料:
2 4 7 9
1 3 6 8
我們要將第二組資料插入到第一組資料中,按照之前的說法,找到第一個不比待插入資料小的資料的前一個數據“i”,但是我們發現第一組資料中第一個資料就符合我們的要求,那麼它自然沒有前一個數據。這裡我們給出一個方案:在進行插入工作前,先對兩個連結串列的首元素進行比較,如果連結串列一的首元素是小於第二個連結串列的首元素的,那麼開始合併工作,如果連結串列一的首元素不小於第二個連結串列的首元素,我們先交換兩個連結串列的頭結點,使得連結串列一變成連結串列二。但是這種方式僅適用於我們當前的情況,因為我們要實現的是將連結串列二的元素加入到連結串列一中,之後銷燬連結串列二,這就意味著我們最後只要得到一個連結串列就可以,只要使得傳遞過來的連結串列一在執行完我們的合併連結串列程式後能夠變成合並後的結果連結串列就好了,因此我們可能要更改傳遞過來的連結串列的頭結點的值。
根據上面的說法,我們就要在開始進行合併之前先對兩個連結串列的首元素進行比較,如果連結串列二的首元素比連結串列一的首元素大,則交換兩個連結串列的控制頭,再執行後續的合併工作。
解決了上述問題,還差最後一種情況,我們來觀察下面一組資料:
1 3 4 6 7
2 6 9 10 13
我們可以觀察到,將第二組資料依次向第一組資料中插入,到了“9”的時候,第一組資料就沒有可以插入的位置了,而應該在尾部追加,於是,我們就不能執行之前的操作了,而應該執行另外一項操作,我們具體實現是這樣的:
如果我們在第一組資料中找不到比待插入資料大的資料,我們就將待插入資料的後面的資料連同待插入資料本身,追加到第一組資料的末尾。
根據如上分析,我們需要執行的有四種對連結串列的操作:1、在指定鏈中找到第一個比目標資料的大的節點的前驅節點; 2、將指定元素插入到指定連結串列的指定位置; 3、將連結串列中的制定位置後的所有元素追加到指定連結串列的末尾; 4、交換兩個連結串列的頭結點。
下面我們就分佈解決這四種操作(注:A_LINE是我們自己定義的為了方便起見的一個結構體,只有一個int型別的num和一個A_LINE *型別的next成員不具有任何意義,僅僅是為了完成合並連結串列):
1、在指定鏈中找到第一個比目標資料的大的節點的前驅節點:
A_LINE *getFirstLocalBiggerThanEle(A_LINE head, A_LINE ele) {
A_LINE *p;
A_LINE *q;
for (p = head.next; p->next != NULL && ele.num >= p->num; p = p->next)
q = p;
if (p->next == NULL) {
return NOT_FOUND;
}
return q;
}
我們用A_LINE *p來遍歷連結串列一,用A_LINE *q來實時儲存當前節點的前驅節點,連結串列沒有遍歷完而且當前元素依然小於等於目標元素,則迴圈繼續。當迴圈結束後,判斷迴圈結束的原因,如果是因為連結串列遍歷完了而結束的迴圈,則說明整個連結串列裡都沒有比目標節點大的節點,返回NOT_FOUND;若是因為找到了比目標元素大的元素而結束的,那麼返回該元素的前驅節點的地址“q”。
2、將指定元素插入到指定連結串列的指定位置:
void insertEleToLine(A_LINE *local, A_LINE ele) {
A_LINE *newEle;
newEle = (A_LINE *)calloc(1, sizeof(A_LINE));
newEle->next = local->next;
local->next = newEle;
newEle->num = ele.num;
}
在第一步中我們已經可以得到需要插入的位置的首地址了,下一步我們就要完成插入了,這對於學過連結串列的人並不是什麼難事,本文不做為重點講解。
3、將一段資料追加到指定連結串列的末尾:
void appendLineToLineEnd(A_LINE *line, A_LINE targetLine) {
A_LINE *tarP;
for (tarP = targetLine.next; tarP->next != NULL; tarP = tarP->next)
;
tarP->next = line;
}
這裡給出的A_LINE *line代表著待插入連結串列的首元素的首地址,targetLine代表要插入到的連結串列的頭結點。首先先定位目標連結串列的末節點的首地址,再將待插入連結串列的末節點的next成員更改為待插入連結串列的首元素的首地址。
但這裡存在著問題,這裡的賦值相當於直接把連結串列二的一部分節點直接放到連結串列一的末尾,並不是複製出來一份再追加到連結串列一的末尾,這樣就使得連結串列二的那一部分節點又屬於連結串列一又屬於連結串列二,而連結串列二在最後是要被釋放的,那麼它的後幾個節點也會被釋放。為了防止這樣的情況發生,我們必須將連結串列二中的要插入到連結串列一中的那一部分節點的前驅節點的next成員賦值為NULL,這樣在就相當於將那一部分節點從連結串列二中刪除,但是這樣並不會引起記憶體洩漏,這些節點被連線到了連結串列一的末尾,因此在釋放連結串列一的時候這些節點依然會被釋放。
4、交換兩個連結串列的頭結點:
void exchangeLine(A_LINE *head1, A_LINE *head2) {
A_LINE temp;
temp = *head1;
*head1 = *head2;
*head2 = temp;
}
5、合併連結串列完整程式碼:void margeLine(A_LINE *targetLine, A_LINE *resourceLine) {
A_LINE *tarP = targetLine->next;
A_LINE *srcP = resourceLine->next;
A_LINE *srcPreP;
A_LINE *local;
if (tarP->num >= srcP->num) {
exchangeLine(targetLine, resourceLine);
}
for (srcP = srcPreP = resourceLine->next; srcP != NULL; srcP = srcP->next) {
local = getFirstLocalBiggerThanEle(*targetLine, *srcP);
if (local != NOT_FOUND) {
insertEleToLine(local, *srcP);
}
else {
appendLineToLineEnd(srcPreP->next, *targetLine);
srcPreP->next = NULL;
destoryLine(resourceLine);
return;
}
srcPreP = srcP;
}
destoryLine(resourceLine);
}
最後我們給出整體的可測試的程式碼:
#include<stdio.h>
#include<malloc.h>
#define NOT_FOUND NULL
typedef struct A_LINE {
int num;
struct A_LINE *next;
} A_LINE;
void margeLine(A_LINE *targetLine, A_LINE *resourceLine);
A_LINE *getFirstLocalBiggerThanEle(A_LINE head, A_LINE ele);
void insertEleToLine(A_LINE *local, A_LINE ele);
void destoryLine(A_LINE *head);
void initLine(A_LINE *head);
void showLine(A_LINE head);
void exchangeLine(A_LINE *head1, A_LINE *head2);
void appendLineToLineEnd(A_LINE *line, A_LINE targetLine);
void appendLineToLineEnd(A_LINE *line, A_LINE targetLine) {
A_LINE *tarP;
for (tarP = targetLine.next; tarP->next != NULL; tarP = tarP->next)
;
tarP->next = line;
}
void exchangeLine(A_LINE *head1, A_LINE *head2) {
A_LINE temp;
temp = *head1;
*head1 = *head2;
*head2 = temp;
}
void showLine(A_LINE head) {
A_LINE *p;
for (p = head.next; p != NULL; p = p->next) {
printf("%d ", p->num);
}
}
void initLine(A_LINE *head) {
int num;
A_LINE *p = NULL;
printf("請輸入一個數(-1表示結束):");
scanf_s("%d", &num);
while (num != -1) {
if (head->next == NULL) {
head->next = (A_LINE *)calloc(1, sizeof(A_LINE));
(head->next)->num = num;
p = head->next;
}
else {
p->next = (A_LINE *)calloc(1, sizeof(A_LINE));
(p->next)->num = num;
p = p->next;
}
printf("請輸入一個數(-1表示結束)");
scanf_s("%d", &num);
}
}
void destoryLine(A_LINE *head) {
A_LINE *p = head->next;
while (NULL != head->next) {
p = head->next;
head->next = p->next;
free(p);
}
}
void insertEleToLine(A_LINE *local, A_LINE ele) {
A_LINE *newEle;
newEle = (A_LINE *)calloc(1, sizeof(A_LINE));
newEle->next = local->next;
local->next = newEle;
newEle->num = ele.num;
}
A_LINE *getFirstLocalBiggerThanEle(A_LINE head, A_LINE ele) {
A_LINE *p = NULL;
A_LINE *q = NULL;
for (p = head.next; p != NULL && ele.num >= p->num; p = p->next)
q = p;
if (p == NULL) {
return NOT_FOUND;
}
return q;
}
void margeLine(A_LINE *targetLine, A_LINE *resourceLine) {
A_LINE *tarP = targetLine->next;
A_LINE *srcP = resourceLine->next;
A_LINE *srcPreP;
A_LINE *local;
if (tarP->num >= srcP->num) {
exchangeLine(targetLine, resourceLine);
}
for (srcP = srcPreP = resourceLine->next; srcP != NULL; srcP = srcP->next) {
local = getFirstLocalBiggerThanEle(*targetLine, *srcP);
if (local != NOT_FOUND) {
insertEleToLine(local, *srcP);
}
else {
appendLineToLineEnd(srcPreP->next, *targetLine);
srcPreP->next = NULL;
destoryLine(resourceLine);
return;
}
srcPreP = srcP;
}
destoryLine(resourceLine);
}
void main(void) {
A_LINE head1 = {0};
A_LINE head2 = {0};
printf("錄入第一個連結串列:\n");
initLine(&head1);
printf("錄入第二個連結串列\n");
initLine(&head2);
margeLine(&head1, &head2);
printf("連結串列一:\n");
showLine(head1);
printf("連結串列二:\n");
showLine(head2);
destoryLine(&head1);
}