不固定結點數的二叉樹建樹
技術標籤:資料結構
雙向連結串列
雙向連結串列,即雙向帶頭迴圈連結串列,結構最複雜,一般用在單獨儲存資料。它有兩個指標,一個指向前驅結點的前驅指標,一個指向後置結點的後置指標,並且還儲存著相應的資料。
邏輯結構如圖所示:
雙向連結串列的宣告
typedef int LTDataType;
typedef struct ListNode
{
LTDataType data;
struct ListNode* next;
struct ListNode* prev;
}ListNode;
雙向連結串列的介面實現
1. ListNode* BuyListNode(LTDataType x)
描述:申請一個雙向連結串列的結點
實現:
ListNode* BuyListNode(LTDataType x){
ListNode* newnode = (ListNode*)malloc(sizeof(ListNode));
newnode->data = x;
newnode->next = NULL;
newnode->prev = NULL;
return newnode;
}
2. ListNode* ListInit()
描述:對雙向連結串列進行初始化
實現:
ListNode* ListInit(){
ListNode* phead = (ListNode*)malloc(sizeof(ListNode));
phead->next = phead;
phead->prev = phead;
return phead;
}
3. void ListPrint(ListNode* plist)
描述:對雙向連結串列進行列印
實現:
void ListPrint(ListNode* plist){
assert(plist) ;
ListNode* cur = plist->next;
while(cur != plist)
{
printf("%d ",cur->data);
cur = cur->next;
}
printf("\n");
}
4. void ListInsert(ListNode* pos, LTDataType x)
描述:在pos位置之前進行插入
實現:
void ListInsert(ListNode* pos, LTDataType x){
assert(pos);
ListNode* newnode = BuyListNode(x);
ListNode* posPrev = pos->prev;
posPrev->next = newnode;
newnode->prev = posPrev;
newnode->next = pos;
pos->prev = newnode;
}
5.void ListErase(ListNode* pos)
描述:刪除pos位置的結點
實現:
void ListErase(ListNode* pos){
assert(pos);
ListNode* posPrev = pos->prev;
ListNode* posNext = pos->next;
free(pos);
posPrev->next = posNext;
posNext->prev = posPrev;
}
6. void ListPushFront(ListNode* plist, LTDataType x)
描述:對雙向連結串列進行頭插
實現:
方法1:
void ListPushFront(ListNode* plist, LTDataType x){
assert(plist);
ListNode* newnode = BuyListNode(x);
ListNode* next = plist->next;
plist->next = newnode;
newnode->prev = plist;
newnode->next = next;
next->prev = newnode;
}
方法2:
ListInsert(plist->next,x);
7.void ListPushBack(ListNode* plist, LTDataType x)
描述:雙向連結串列尾插
實現:
方法1:
void ListPushBack(ListNode* plist, LTDataType x){
assert(plist);
ListNode* tail = plist->prev;
ListNode* newnode = BuyListNode(x);
//尾的下一個指向新結點,新結點的前一個指向尾,新結點的下一個指向頭,頭結點的前>一個指向新結點
tail->next = newnode;
newnode->prev = tail;
newnode->next = plist;
plist->prev = newnode;
}
方法2:
ListInsert(plist,x);
8.void ListPopFront(ListNode* plist)
描述:雙向連結串列頭刪
實現:
方法1:
void ListPopFront(ListNode* plist){
assert(plist);
//若無資料,則不刪除
assert(plist->prev!=plist);
ListNode* popNode = plist->next;
ListNode* next = popNode->next;
free(popNode);
plist->next = next;
next->prev = plist;
}
方法2:
ListErase(plist->next);
9. void ListPopBack(ListNode* plist)
描述:雙向連結串列尾刪
實現:
方法1:
void ListPopBack(ListNode* plist){
assert(plist);
ListNode* tail = plist->prev;
ListNode* tailPrev = tail->prev;
//如果最後一個數據刪完以後,連結串列恢復到最開始狀態,只有頭結點,並且是迴圈狀態
free(tail);
tailPrev->next = plist;
plist->prev = tailPrev;
}
方法2:
ListErase(plist->prev);
順序表和連結串列概念選擇題
- 在一個長度為n的順序表中刪除第i個元素,要移動_______個元素。如果要在第i個元素前插入一個元素,要後
移_________個元素。
A n-i,n-i+1
B n-i+1,n-i
C n-i,n-i
D n-i+1,n-i+1
A,刪除第 i 個元素,即將 i 之後的元素移動到 i 的位置,總共需要移動 n - i 次,而如果要在第 i 個元素前插入一個元素,則需要將包含 i 在內的元素,向後移動 1 個單位,則共需移動 n - i + 1
- 取順序表的第 i 個元素的時間同i的大小有關()
A 對
B 錯
B,順序表是支援下標訪問的,與 i 的大小無關
- 在一個具有 n 個結點的有序單鏈表中插入一個新結點並仍然保持有序的時間複雜度是( ) 。
A O(1)
B O(n)
C O(n2)
D O(nlog2n)
B,單鏈表是有序的,則插入的新結點,需要和從連結串列的頭結點開始一一比較大小,直到找到小於(大於)的結點,其中最壞的情況就是一直比較到連結串列末尾,都沒有結果,這時說明該新結點為最大值(最小值),遍歷連結串列的次數為n次,時間複雜度為o(n)。
- 下列關於線性連結串列的敘述中,正確的是( )。
A 各資料結點的儲存空間可以不連續,但它們的儲存順序與邏輯順序必須一致
B 各資料結點的儲存順序與邏輯順序可以不一致,但它們的儲存空間必須連續
C 進行插入與刪除時,不需要移動表中的元素
D 以上說法均不正確
C,連結串列在進行刪除或插入時,是不需要移動表中的元素,只需要變化連結串列上對應的指標就行。
- 設一個連結串列最常用的操作是在末尾插入結點和刪除尾結點,則選用()最節省時間。
A 單鏈表
B 單迴圈連結串列
C 帶尾指標的單迴圈連結串列
D 帶頭結點的雙迴圈連結串列
D,雙向連結串列的頭結點可以通過前驅指標直接訪問尾結點,而單鏈表則只能通過next指標,遍歷到尾指標。
- 連結串列不具有的特點是()。
A 插入、刪除不需要移動元素
B 不必事先估計儲存空間
C 可隨機訪問任一元素
D 所需空間與線性表長度成正比
C,連結串列不能隨機訪問元素
- 在一個單鏈表中,若刪除 P 所指結點的後續結點,則執行( )?
A p = p->next;p->next = p->next->next;
B p->next = p->next;
C p->next = p->next->next;
D p = p->next->next
C,要刪除 p 的後一個結點,只需將 p 的 next 指標指向後一個結點的下一個結點,即指向p->next->next即可。
- 一個單向連結串列佇列中有一個指標p,現要將指標r插入到p之後,該進行的操作是____。
A p->next=p->next->next
B r->next=p;p->next=r->next
C r->next=p->next;p->next=r
D r=p->next;p->next=r->next
E r->next=p;p->next=r
F p=p->next->next
C,首先要讓 r 的next指標儲存 p 的next指標,然後再將 p 的next 指向 r ,這樣插入後,順序就不會變。