1. 程式人生 > 其它 >【資料結構篇】連結串列(c語言)

【資料結構篇】連結串列(c語言)


title: 【資料結構篇】連結串列
date: 2022-02-15 10:32:54
tags:
- 資料結構
- 連結串列
- C語言
categories:
- 資料結構
- 連結串列

cover: https://cdn.jsdelivr.net/gh/Skye-rs/CDN/cover/cover3.jpg


【資料結構篇】連結串列

設計連結串列

  • 在連結串列類中實現這些功能:

·get(index):獲取連結串列中第 index 個節點的值。如果索引無效,則返回-1。
·addAtHead(val):在連結串列的第一個元素之前新增一個值為 val 的節點。插入後,新節點將成為連結串列的第一個節點。
·addAtTail(val):將值為 val 的節點追加到連結串列的最後一個元素。
·addAtIndex(index,val):在連結串列中的第 index 個節點之前新增值為 val 的節點。如果 index 等於連結串列的長度,則該節點將附加到連結串列的末尾。如果 index 大於連結串列長度,則不會插入節點。如果index小於0,則在頭部插入節點。
·deleteAtIndex(index):如果索引 index 有效,則刪除連結串列中的第 index 個節點。

  • 程式碼示例如下
typedef struct MyLinkedList_t
{
	int val;
	struct MyLinkedList_t *next;
} MyLinkedList;

/** Initialize your data structure here. */

MyLinkedList *myLinkedListCreate()
{
	MyLinkedList *obj = (MyLinkedList *)malloc(sizeof(MyLinkedList));
	obj->val = 0;
	obj->next = NULL;
	return obj;
}

/** Get the value of the index-th node in the linked list. If the index is invalid, return -1. */
int myLinkedListGet(MyLinkedList *obj, int index)
{
	if (index < 0 || obj->next == NULL)
	{
		return -1;
	}
	
	int now = 0;
	MyLinkedList *listNow = obj->next;
	while (now < index)
	{
		if (listNow == NULL)
		{
			return -1;
		}

		listNow = listNow->next;
		now++;
	}

	if (listNow != NULL)
	{
		return listNow->val;
	}

	return -1;
}

/** Add a node of value val before the first element of the linked list. After the insertion, the new node will be the first node of the linked list. */
void myLinkedListAddAtHead(MyLinkedList *obj, int val)
{
	MyLinkedList *Node = (MyLinkedList *)malloc(sizeof(MyLinkedList));
	Node->val = val;
	Node->next = NULL;

	if (obj->next == NULL)
	{
		obj->next = Node;
		return;
	}
	else
	{
		Node->next = obj->next;
		obj->next = Node;
	}
}

/** Append a node of value val to the last element of the linked list. */
void myLinkedListAddAtTail(MyLinkedList *obj, int val)
{
	MyLinkedList *Node = (MyLinkedList *)malloc(sizeof(MyLinkedList));
	Node->val = val;
	Node->next = NULL;

	MyLinkedList *nowList = obj;
	while (nowList->next != NULL)
	{
		nowList = nowList->next;
	}

	nowList->next = Node;
}

/** Add a node of value val before the index-th node in the linked list. If index equals to the length of linked list, the node will be appended to the end of linked list. If index is greater than the length, the node will not be inserted. */
void myLinkedListAddAtIndex(MyLinkedList *obj, int index, int val)
{
	if (index <= 0)
	{
		myLinkedListAddAtHead(obj, val);
	}

	int now = 0;
	MyLinkedList *nowList = obj->next;
	while (nowList->next != NULL)
	{
		if (now == index - 1)
		{
			break;
		}
		nowList = nowList->next;
		now++;
	}

	if (index - 1 != now)
	{
		return;
	}

	MyLinkedList *Node = (MyLinkedList *)malloc(sizeof(MyLinkedList));
	Node->val = val;
	Node->next = nowList->next;
	nowList->next = Node;
}

/** Delete the index-th node in the linked list, if the index is valid. */
void myLinkedListDeleteAtIndex(MyLinkedList *obj, int index)
{
	MyLinkedList *r;
	if (index < 0 || obj->next == NULL)
	{
		return;
	}

	if (index == 0)
	{
		r = obj->next;
		obj->next = obj->next->next;
		free(r);
		return;
	}

	MyLinkedList *nowList = obj->next;
	int now = 0;
	while (nowList->next != NULL)
	{
		if (now == index - 1)
		{
			break;
		}
		nowList = nowList->next;
		now++;
	}

	if (now == index - 1 && nowList->next != NULL)
	{
		r = nowList->next;
		nowList->next = nowList->next->next;
		free(r);
	}
}

void myNodeFree(MyLinkedList *Node)
{
    if (Node->next != NULL)
{
    myNodeFree(Node->next);
    Node->next = NULL;
}
free(Node);
}

void myLinkedListFree(MyLinkedList *obj)
{//從最後一個結點向前逐漸釋放,這裡用遞迴實現
    myNodeFree(obj);
}

環形連結串列

  • 可以用快慢指標判斷是否有環(有環返回真,無環返回假)
typedef struct {
      int val;
      struct ListNode *next;
  }ListNode;

bool hasCycle(struct ListNode *head) {
        if(head==NULL) return false;
        ListNode *f=head;
        ListNode *s=head;
        do{
            if(f->next==NULL||f->next->next==NULL) return false;
            f=f->next->next;//f為走兩步的快指標
            s=s->next;//s為走一步的慢指標
        }while(f!=s);
        return true;//如果有環一定能相遇
    }
  • 如果要返回入環的第一個結點呢?


可以這樣分析:
1 慢指標走過的路程為a(頭結點到環入口的距離)+b(慢指標在環內的距離)
2 快指標走過的路程為a+b+c(相遇點到換入口的距離)+b
3 因為快指標速度是慢指標的兩倍,所以可以匯出a=c
4 所以只要再安排一個位於頭結點的指標和慢指標同時出發,它們的相遇位置即為環入口

struct ListNode *detectCycle(struct ListNode *head) {
        if(head==NULL) return false;
        struct ListNode *f=head;
        struct ListNode *s=head;
        do{
            if(f->next==NULL||f->next->next==NULL) return false;
            f=f->next->next;//f為走兩步的快指標
            s=s->next;//s為走一步的慢指標
        }while(f!=s);
       struct ListNode*p=head;
       while(p!=s)
       {
           p=p->next;
           s=s->next;
       }
        return p;
}

迴文連結串列

struct ListNode* reverseList(struct ListNode* head) {
    struct ListNode* prev = NULL;
    struct ListNode* curr = head;
    while (curr != NULL) {
        struct ListNode* nextTemp = curr->next;
        curr->next = prev;
        prev = curr;
        curr = nextTemp;
    }
    return prev;
}//反轉連結串列

struct ListNode* endOfFirstHalf(struct ListNode* head) {
    struct ListNode* fast = head;
    struct ListNode* slow = head;
    while (fast->next != NULL && fast->next->next != NULL) {
        fast = fast->next->next;
        slow = slow->next;
    }
    return slow;
}

bool isPalindrome(struct ListNode* head) {
    if (head == NULL) {
        return true;
    }

    // 找到前半部分連結串列的尾節點並反轉後半部分連結串列
    struct ListNode* firstHalfEnd = endOfFirstHalf(head);
    struct ListNode* secondHalfStart = reverseList(firstHalfEnd->next);

    // 判斷是否迴文
    struct ListNode* p1 = head;
    struct ListNode* p2 = secondHalfStart;
    bool result = true;
    while (result && p2 != NULL) {
        if (p1->val != p2->val) {
            result = false;
        }
        p1 = p1->next;
        p2 = p2->next;
    }

    // 還原連結串列並返回結果
    firstHalfEnd->next = reverseList(secondHalfStart);
    return result;
}

持續更新中