一、已知線性表中元素以值遞增有序排列,並以單鏈表作存儲結構。試寫一高效的算法,刪除表中所有值大於mink且小於maxk的元素(若表中存在這樣的元素)同時釋放被刪結點空間,並分析你的算法的時間復雜度(注意:mink和maxk是給定的兩個參變量,它們的值可以和表中的元素相同,也可以不同)。
答:
本題對於算法的優化的突破點在於線性表中的元素是以值遞增有序排列。從頭節點開始遍歷鏈表,當遍歷到第一個大於mink的元素時,記錄下來其前驅的指針。然后繼續判斷后面的元素。如果后面的元素小於maxk繼續遍歷,同時釋放該結點。當判斷出來的元素大於等於maxk時,需要將原先記錄的結點的指針的next指向這個元素的后繼指針,同時將這個結點空間釋放,然后結束循環,返回鏈表頭節點。本題在輸入的時候需要判斷這個線性表是否為空表。
本算法實現的偽代碼如下:
/*
函數名稱:有序鏈表刪除算法
傳入參數:需要插入的單鏈表L, 操作區間mink, maxk
返回值:返回OK代表刪除成功,返回NO代表傳入為空表或者刪除后為空表
*/
Status DeleteLNode(LNode &L, mink, maxk) {
if(L->next == NULL) {
//說明傳入的是空表
return NO;
}
else {
p1 = L->next; //p1指向第一個結點
p2 = L->next //p2指向第一個結點
//查找第一個大於mink結點所處在的位置
while(p1 && p1->val <= mink) {
p2 = p1;
p1 = p1->next;
}
while(p1 && p1->val <= maxk) {
temp = p1;
p1 = p1->next;
free(temp); //釋放空間
}
/*此時p1為第一個大於maxn的結點或者空結點因為可能不存在大於等於maxk的元素*/
p2->next = p1
if(L->next != NULL)
return OK;
else
return NO;
}
}
二、試寫一算法,實現順序表的就地逆置,即利用原表的存儲空間將線性表(a1, a2, ...,an)逆置為(an, an-1,...,a1)
答:
由於我們可以通過標號來訪問順序表,所以解決本問題我們只需要遍歷數組一半的元素將第i個元素和第L.length-i-1個元素交換即可。
本算法實現的偽代碼如下:
/*
函數名稱:順序表逆置算法
傳入參數:順序表
返回值:返回OK代表操作成功
*/
Status ListOppose(SqList &L)
{ //顛倒順序表中的數據元素
int i;
ElemType x;
for(i = 0; i< L.length / 2; i++)
{
/*實現數據元素交換*/
x = L.elem[i];
L.elem[i] = L.elem[L.length-i-1];
L.elem[L.length-i-1] = x;
}
return OK;
}
三、試寫一算法,實現對單鏈表就地置換。
答:
首先我們需要將鏈表的頭節點取出,構成一個空表。然后將剩下的元素依次取出作為前一個鏈表的第一個元素節點。最后將頭節點的指針指向第一個元素結點。
本算法實現的偽代碼如下:
/*
函數名稱:單鏈表表逆置算法
傳入參數:單鏈表
返回值:返回OK代表操作成功
*/
Status LNodeOppose(LNode &L)
{ // 顛倒單鏈表中的數據元素
temp1 = L->next;
temp1->next = NULL;
while(temp1) {
temp2 = temp1->next; //得到后繼
temp1->next = L->next;
L->next = temp1; //插入在都節點之后
temp1 = temp2; //向后移動
}
return OK;
}
四、假設有兩個按元素值遞增有序排列的線性表A和B,均以單鏈表作存儲結構,請編寫算法將A表和B表歸並成一個按元素值遞減有序(即非遞增有序,允許表中含有值相同的元素)排列的線性表C,並要求利用原表(即A表和B表)的結點空間構造C表。
答:
解決這個問題,我們需要使用兩個指針指向A和B兩個鏈表,將A和B中第一個元素較小的結點取出構成第三個鏈表最后的結點。然后在遍歷的過程中使用頭插法一次插入。
本算法實現的偽代碼如下:
/*
函數名稱:鏈表合並算法
傳入參數:兩鏈表A和B
返回值:返回OK代表操作成功
*/
Status Merge(LNode &A, LNode &B)
{
tempA = A->next;
tempB = B->next;
C = NULL;
//實現對齊長度的插入
while(A->next != NULL && B->next != NULL) {
tempA = A->next;
tempB = B->next;
if(tempA->val <= tempB->val) {
//將小的插入C中
A->next = tempA->next;
tempA->next = NULL;
tempA->next = C->next;
C->next = tempA;
}
else {
B->next = tempB->next;
tempB->next = NULL;
tempB->next = C->next
}
}
//如果A表更長
if(A->next == NULL) {
while(B->next != NULL) {
tempB = B->next;
B->next = tempB->next;
tempB->next = C->next;
C->next = tempB;
}
}
//如果B表更長
else {
while(A->next != NULL) {
tempA = A->next;
A->next = tempA->next;
tempA->next = C->next;
C->next = tempA;
}
}
return OK;
}
Status ListMergeOppose_L(LinkList &A,LinkList &B,LinkList &C){
LinkList pa,pb,qa,qb;
pa=A;
pb=B;
qa=pa; // 保存pa的前驅指針
qb=pb; // 保存pb的前驅指針
pa=pa->next;
pb=pb->next;
A->next=NULL;
C=A;
while(pa&&pb){
if(pa->data<pb->data){
qa=pa;
pa=pa->next;
qa->next=A->next;
A->next=qa;
} else{
qb=pb;
pb=pb->next;
qb->next=A->next; //將當前最小結點插入A表表頭
A->next=qb;
}
}
while(pa){
qa=pa;
pa=pa->next;
qa->next=A->next;
A->next=qa;
}
while(pb){
qb=pb;
pb=pb->next;
qb->next=A->next;
A->next=qb;
}
pb=B;
free(pb);
return OK;
}
五、已知由一個線性鏈表表示的線性表中含有三類字符的數據元素(如:字母字符、數字字符和其它字符),試編寫算法將該線性鏈表分割為三個循環鏈表,其中每個循環鏈表示的線性表中均只含一類字符。
答:
由分析可知,我們需要通過遍歷判斷這個字符屬於哪一類。然后通過將其插入相應的位置構成鏈表。最后將其轉換為循環鏈表。
本算法實現的偽代碼如下:
/*
函數名稱:分割鏈表
傳入參數:需分割鏈表L 三結果鏈表A和B和C
返回值:返回OK代表操作成功
*/
Status Classify(LNode &L, LNode &A, LNode &B, LNode &C)
{
temp = L->Node;
//使A,B,C成為循環鏈表
A->next = A;
B->next = B;
C->next = C;
while(temp) {
//通過ASCILL判斷
if(temp->data是字母字符) {
//將該字符插入鏈表A中
temp->next = A->next;
A->next = temp;
A = temp;
}
else if(temp->data是數字) {
//將該字符插入鏈表A中
temp->next = B->next;
B->next = temp;
B = temp;
}
else{
//將該字符插入鏈表A中
temp->next = C->next;
C->next = temp;
C = temp;
}
temp = temp->next;
}
return OK;
}
