Data structure sequence table -- bidirectional leading circular linked list


The structure of two-way leading circular linked list is shown in the figure

Definition of bidirectional circular linked list structure

typedef int LTDataType;
typedef struct ListNode
{
	LTDataType data;
	struct ListNode* prev;
	struct ListNode* next;
}LTNode;

Note that the structure definition does not reflect whether the linked list has rings.

The interfaces to be implemented for the two-way leading circular linked list are:

  1. Create linked list and return header node (initialization)
  2. Destroy
  3. Print
  4. Tail insertion
  5. Head insert
  6. Tail deletion
  7. Header deletion
  8. lookup
  9. Insert in the previous position of pos
  10. Delete pos node

Create linked list and initialize

Note that a header node (sentinel node) is created here, and no data is saved.

LTNode* IistInit()
{
	LTNode* phead = (LTNode*)malloc(sizeof(LTNode));
	phead->next = phead;
	phead->prev = phead;
	
	return phead;
}

Open up new nodes

LTNode* BuyListNode(LTDataType x)
{
	LTNode* newnode = (LTNode*)malloc(sizeof(LTNode));
	newnode->data = x;
	newnode->next = NULL;
	newnode->prev = NULL;
	
	return newnode;
}

Print bidirectional linked list

Note that the loop end condition is cur=phead

void ListPrint(LTNode* phead)
{
	assert(phead);
	LTNode* cur = phead->next;
	while(cur != phead)
	{
		printf("%d ", cur->data);
		cur = cur->next;
	}
	printf("\n");
}
	

Bidirectional linked list tail insertion

The time complexity is O(1), which is more efficient than single linked list and the same as sequential list

void ListPushBack(LTNode* phead, LTDataType x)
{
	assert(phead);
	LTNode* tail = phead->prev;
	LTNode* newnode = BuyListNode(x);
	tail->next = newnode;
	newnode->prev = tail;
	phead->prev = newnode;
	newnode->next = phead;
}

Bidirectional linked list tail deletion

void ListPopBack(LTNode* phead)
{
	assert(phead);
	assert(phead->next != phead);
	LTNode* tail = phead->prev;
	LTNode* tailPrev = tail->prev;
	free(tail);
	tailPrev->next = phead;
	phead->prev = tailPrev;
}

Bidirectional chain header insertion

void ListPushFront(LTNode* phead, LTDataType x)
{
	assert(phead);
	LTNode* newnode = BuyLsitNode(x);
	LTNode* next = phead->next;
	phead->next = newnode;
	newnode->prev = phead;
	newnode->next = next;
	next->prev = newnode;
}

Bidirectional linked list header deletion

The prerequisite for deletion is that the linked list is not empty

void ListPopFront(LTNode* phead)
{
	assert(phead);
	assert(phead->next != phead);
	LTNode* next = phead->next;
	LTNode* nextNext = next->next;
	phead->next = nextNext;
	nextNext->prev = phead;
	free(next);
}

Bidirectional linked list lookup

Node with return value of x
NULL if not found

LTNode* ListFind(LTNode* phead, LTDataType x)
{
	assert(phead);
	LTNode* cur = phead->next;
	while(cur != phead)
	{
		if(cur->data == x)
		{
			return cur;
		}
		cur = cur->next;
	}
	return NULL;
}

Insert in the previous position of pos

The default pos is in the linked list

If it is not a two-way linked list, it cannot be implemented (but it can also be implemented without a header node)

void ListInsert(LTNode* phead, LTDataType x)
{
	assert(pos);
	LTNode* posPrev = pos->prev;
	LTNode* newnode = BuyListNode(x);
	posPrev->next = newnode;
	newnode->prev = posPrev;
	newnode->next = pos;
	pos->prev = newnode;
}

Delete pos node

Here, the default pos is in the linked list, and pos cannot be a sentinel node, so there are at least phead and pos in the linked list

void ListErase(LTNode* pos)
{
	assert(pos);
	LTNode* posPrev = pos->prev;
	LTNode* posNext = pos->next;
	posPrev->next = posNext;
	posNext->prev = posPrev;
	free(pos);
}

Destroy linked list

Before free, you still need to record a node. The difference from the single linked list is that the loop end condition and the free sentinel node are required. Note that phead is not set to null, and the user needs to set phead to null outside the function. Similar to free.

void LsitDestroy(LTNode* phead)
{
	assert(phead);
	LTNode* cur = phead->next;
	while(cur != phead)
	{
		LTNode* next = cur->next;
		free(cur);
		cur = next;
	}
	free(phead);
}

Reuse ListInsert to implement head and tail insertion

Head insert

void ListPushFront(LTNode* phead, LTDataType x)
{
	ListInsert(phead->next, x);
}

Tail insertion

void ListPushBack(LTNode* phead, LTDataType x)
{
	ListInsert(phead, x);
}

Implementation of header deletion and tail deletion by reusing LsitErase

Header deletion
Note that assertions need to be preserved.
Previous header deletion

assert(phead->next != phead);

It is used to ensure that the linked list is not empty. Here, it is ensured that the pos passed to ListErase is not phead

void ListPopFront(LTNode* phead)
{
	assert(phead);
	assert(phead->next !=phead);
	ListErase(phead->next);
}

Tail deletion

void ListPopBack(LTNode* phead)
{
	assert(phead);
	assert(phead->next != phead);
	ListErase(phead->prev);
}

summary

The time complexity of two-way circular linked list is O(1), which fully shows the advantages of linked list.
In addition to random access, the efficiency is higher than that of sequential table.
Because the previous node can be accessed, tail insertion, middle insertion, tail deletion and middle deletion are simpler than single linked lists.

Tags: data structure linked list

Posted on Fri, 05 Nov 2021 17:12:02 -0400 by captainplanet17