【数据结构】顺序表和链表

1、线性表

线性表(linear list)是n个具有相同特性的数据元素的有限序列。 线性表是一种在实际中广泛使用的数据结构,常见的线性表:顺序表、链表、栈、队列、字符串...

线性表在逻辑上是线性结构,也就说是连续的一条直线。但是在物理结构上并不一定是连续的,线性表在物理上存储时,通常以数组和链式结构的形式存储。

 2、顺序表

2.1 概念及结构

顺序表是用一段物理地址连续的存储单元依次存储数据元素的线性结构,一般情况下采用数组存储。在数组上完成数据的增删查改。

顺序表一般可以分为:

  • 静态顺序表:使用定长数组存储元素。

//顺序表的静态存储
#define N 7
typedef int SLDatatype;
typedef struct SeqList
{
	SLDatatype arr[N];//定长数组
	int size;//有效数据的个数
}SeqList;
  • 动态顺序表:使用动态开辟的数组存储。
//顺序表的动态存储
typedef struct SeqList
{
	SLDatatype* arr;//指向动态开辟的数组
	int size;//有效数据的个数
	int capacity;//容量空间的大小
}SeqList;

2.2 实现顺序表的代码

2.2.1 SeqList.h

#include <stdio.h>
#include <stdlib.h>
#include <assert.h>

//动态顺序表
typedef int SLDatatype;
typedef struct SeqList
{
	SLDatatype* a;//指向动态开辟的数组
	int size;//存储的有效数据个数
	int capacity;//容量
}SL;

//初始化
void SLInit(SL* psl);
//销毁
void SLDestroy(SL* psl);
//打印
void SLPrint(SL* psl);
//尾插
void SLPushBack(SL* psl, SLDatatype x);
//头插
void SLPushFront(SL* psl, SLDatatype x);
//尾删
void SLPopBack(SL* psl);
//头删
void SLPopFront(SL* psl);
//在pos位置插入x
void SLInsert(SL* psl, int pos, SLDatatype x);
//删除pos位置的值
void SLErase(SL* psl, int pos);
//查找
int SLFind(SL* psl, SLDatatype x);
//修改
void SLModify(SL* psl, int pos, SLDatatype x);

2.2.2 SeqList.c

#include "SeqList.h"

void SLInit(SL* psl)
{
	//断言,如果表达式为false,直接终止程序
	//这里我们用它来检查psl指针不为NULL
	assert(psl);

	//malloc分配内存块
	psl->a = (SLDatatype*)malloc(sizeof(SLDatatype) * 4);
	//这里我们检查malloc是否分配成功
	if (psl->a == NULL)
	{
		//perror打印错误信息
		perror("malloc fail");
		return;
	}

	psl->size = 0;
	psl->capacity = 4;
}

void SLDestroy(SL* psl)
{
	assert(psl);

	//动态内存分配,当内存不使用时,应使用free函数将内存块释放
	free(psl->a);
	psl->a = NULL;
	psl->size = 0;
	psl->capacity = 0;
}

void SLPrint(SL* psl)
{
	assert(psl);

	for (int i = 0; i < psl->size; i++)
	{
		printf("%d ", psl->a[i]);
	}
	printf("\n");
}

void SLCheckCapacity(SL* psl)
{
	if (psl->size == psl->capacity)
	{
		//realloc返回指向重新分配的内存块的指针,可能是新位置
		SLDatatype* tmp = (SLDatatype*)realloc(psl->a, sizeof(SLDatatype) * psl->capacity * 2);
		if (tmp == NULL)
		{
			perror("realloc fail");
			return;
		}

		psl->a = tmp;//更新指针
		psl->capacity *= 2;
	}
}

void SLPushBack(SL* psl, SLDatatype x)
{
	assert(psl);

	//方法1:
	//在尾插之前要检查剩余容量是否>=1,不够要扩容
	SLCheckCapacity(psl);
	psl->a[psl->size] = x;
	psl->size++;

	//方法2:
	//SLInsert(psl, psl->size, x);
}

void SLPushFront(SL* psl, SLDatatype x)
{
	assert(psl);

	//方法1:
	SLCheckCapacity(psl);
	int end = psl->size - 1;
	while (end >= 0)
	{
		psl->a[end + 1] = psl->a[end];
		end--;
	}
	psl->a[0] = x;
	psl->size++;

	//方法2:
	//SLInsert(psl, 0, x);
}

void SLPopBack(SL* psl)
{
	assert(psl);

	//方法1:
	psl->a[psl->size - 1] = 0;
	psl->size--;

	//方法2:
	//SLErase(psl, psl->size - 1);
}

void SLPopFront(SL* psl)
{
	assert(psl);

	//方法1:
	int start = 0;
	while (start < psl->size - 1)
	{
		psl->a[start] = psl->a[start + 1];
		start++;
	}
	psl->size--;

	//方法2:
	//SLErase(psl, 0);
}

void SLInsert(SL* psl, int pos, SLDatatype x)
{
	assert(psl);
	//如果pos==psl->size就是尾插
	assert(0 <= pos && pos <= psl->size);

	SLCheckCapacity(psl);
	int end = psl->size - 1;
	while (end >= pos)
	{
		psl->a[end + 1] = psl->a[end];
		end--;
	}
	psl->a[pos] = x;
	psl->size++;
}

void SLErase(SL* psl, int pos)
{
	assert(psl);
	//这里pos不能等于psl->size,因为不能删除不存在的元素
	assert(0 <= pos && pos < psl->size);

	int start = pos + 1;
	while (start < psl->size)
	{
		psl->a[start - 1] = psl->a[start];
		start++;
	}
	psl->size--;
}

int SLFind(SL* psl, SLDatatype x)
{
	assert(psl);

	for (int i = 0; i < psl->size; i++)
	{
		if (psl->a[i] == x)
		{
			return i;
		}
	}

	return - 1;
}

void SLModify(SL* psl, int pos, SLDatatype x)
{
	assert(psl);
	assert(0 <= pos && pos < psl->size);

	psl->a[pos] = x;
}

2.2.3 测试代码

void TestSeqList1()
{
	SL s;
	SLInit(&s);
	SLPushBack(&s, 3);
	SLPushBack(&s, 4);
	SLPushBack(&s, 5);
	SLPushFront(&s, 2);
	SLPushFront(&s, 1);

	SLInsert(&s, 1, 10);
	SLInsert(&s, 4, 11);


	SLPrint(&s);
	SLDestroy(&s);
}

2.3 数组练习

2.3.1 移除元素

27. 移除元素 - 力扣(LeetCode)icon-default.png?t=O83Ahttps://leetcode.cn/problems/remove-element/description/

int removeElement(int* nums, int numsSize, int val)
{
	int src = 0;
	int dst = 0;

	while (dst < numsSize)
	{
		//如果不是要移除的元素就和nums[src]交换
		if (nums[dst] != val)
		{
			nums[src++] = nums[dst++];
		}
		else//如果nums[dst]就是要移除的元素就直接跳过
		{
			dst++;
		}
	}
	return src;
}

2.3.2 合并两个有序数组

88. 合并两个有序数组 - 力扣(LeetCode)icon-default.png?t=O83Ahttps://leetcode.cn/problems/merge-sorted-array/description/

void merge(int* nums1, int nums1Size, int m, int* nums2, int nums2Size, int n) 
{
	int end1 = m - 1;
	int end2 = n - 1;
	int i = m + n - 1;

    //从后往前看
	while (end1 >= 0 && end2 >= 0)
	{
		if (nums1[end1] > nums2[end2])
		{
			nums1[i--] = nums1[end1--];
		}
		else
		{
			nums1[i--] = nums2[end2--];
		}
	}
	while (end2 >= 0)
	{
		nums1[i--] = nums2[end2--];
	}
}

3、链表

3.1 概念及结构

概念:链表是一种物理存储结构上非连续、非顺序的存储结构,数据元素的逻辑顺序是通过链表中的指针链接次序实现的 。

3.2 链表的分类

  • 单向或者双向

  • 带头或者不带头

  • 循环或者非循环

但是最常用的还是:

3.3 实现链表的代码

3.3.1 SList.h

#include <stdio.h>
#include <stdlib.h>
#include <assert.h>

typedef int SLTDataType;
typedef struct SListNode
{
	SLTDataType data;
	struct SListNode* next;
}SLTNode;

//打印
void SLTPrint(SLTNode* phead);
//头插
void SLPushFront(SLTNode** pphead, SLTDataType x);
//尾插
void SLPushBack(SLTNode** pphead, SLTDataType x);
//头删
void SLPopFront(SLTNode** pphead);
//尾删
void SLPopBack(SLTNode** pphead);

//单链表查找
SLTNode* STFind(SLTNode* phead, SLTDataType x);

//在pos之前插入
void SLInsert(SLTNode** pphead, SLTNode* pos, SLTDataType x);

//在pos之后插入
void SLInsertAfter(SLTNode* pos, SLTDataType x);

//删除pos位置的值
void SLErase(SLTNode** pphead, SLTNode* pos);

//删除pos位置后面的值
void SLEraseAfter(SLTNode* pos);

void SLDestroy(SLTNode** pphead);

3.3.2 SList.h

#include "SList.h"

//打印不需要改变头节点,直接传结构体指针即可
void SLTPrint(SLTNode* phead)
{
	SLTNode* cur = phead;
	while (cur != NULL)
	{
		printf("%d->", cur->data);
		cur = cur->next;
	}
	printf("NULL\n");
}

//动态申请一个节点,方便后续的使用
SLTNode* BuyLTNode(SLTDataType x)
{
	SLTNode* newnode = (SLTNode*)malloc(sizeof(SLTNode));
	if (newnode == NULL)
	{
		perror("malloc fail");
		return;
	}
	newnode->data = x;
	newnode->next = NULL;
	return newnode;
}

//头插会改变头节点,所以要传结构体指针的地址,下同
void SLPushFront(SLTNode** pphead, SLTDataType x)
{
	assert(pphead);

	SLTNode* newnode = BuyLTNode(x);
	//如果是第一次插入*pphead=NULL直接让newnode的next指向空就好了
	newnode->next = *pphead;
	*pphead = newnode;
}

void SLPushBack(SLTNode** pphead, SLTDataType x)
{
	assert(pphead);

	SLTNode* newnode = BuyLTNode(x);
	if (*pphead == NULL)
	{
		*pphead = newnode;
		//newnode的next在初始化时已经被置成空了,所以*pphead的next也指向空
	}
	else
	{
		//找到最后一个节点
		SLTNode* tail = *pphead;
		while (tail->next != NULL)
		{
			tail = tail->next;
		}
		tail->next = newnode;
	}
}

void SLPopFront(SLTNode** pphead)
{
	assert(pphead);
	//链表为空的话没有元素可以删除
	assert(*pphead);

	SLTNode* del = *pphead;
	*pphead = (*pphead)->next;//因为->的优先级比*高,所以要加()
	free(del);
}

void SLPopBack(SLTNode** pphead)
{
	assert(pphead);
	assert(*pphead);

	if ((*pphead)->next == NULL)
	{
		free(*pphead);
		*pphead = NULL;
	}
	else
	{
		//找到倒数第二个节点
		SLTNode* tail = *pphead;
		while (tail->next->next != NULL)
		{
			tail = tail->next;
		}
		free(tail->next);
		tail->next = NULL;
	}
}

SLTNode* STFind(SLTNode* phead, SLTDataType x)
{
	SLTNode* cur = phead;
	while (cur)
	{
		if (cur->data == x)
		{
			return cur;
		}
		cur = cur->next;
	}
	return NULL;
}

//在pos位置之前插入
void SLInsert(SLTNode** pphead, SLTNode* pos, SLTDataType x)
{
	assert(pphead);
	assert(pos);

	if (*pphead == pos)
	{
		//相当于头插
		SLPushFront(pphead, x);
	}
	else
	{
		//找到pos的上一个节点
		SLTNode* prev = *pphead;
		while (prev->next != pos)
		{
			prev = prev->next;
		}
		SLTNode* newnode = BuyLTNode(x);
		prev->next = newnode;
		newnode->next = pos;
	}
}
//在pos位置之后插入
void SLInsertAfter(SLTNode* pos, SLTDataType x)
{
	assert(pos);

	SLTNode* newnode = BuyLTNode(x);
	//这两句代码顺序不能颠倒
	newnode->next = pos->next;
	pos->next = newnode;
}

//删除pos位置的值
void SLErase(SLTNode** pphead, SLTNode* pos)
{
	assert(pphead);
	assert(pos);

	if (pos == *pphead)
	{
		//相当于头删
		SLPopFront(pphead);
	}
	else
	{
		SLTNode* prev = *pphead;
		while (prev->next != pos)
		{
			prev = prev->next;
		}
		prev->next = pos->next;
		free(pos);
	}
}

//删除pos位置后面的值
void SLEraseAfter(SLTNode* pos)
{
	assert(pos);
	assert(pos->next);

	SLTNode* next = pos->next;
	pos->next = next->next;
	free(next);
}

void SLDestroy(SLTNode** pphead)
{
	assert(pphead);

	SLTNode* cur = *pphead;
	while (cur)
	{
		SLTNode* next = cur->next;
		free(cur);
		cur = next;
	}
	*pphead = NULL;
}

3.3.3 测试代码

void TestSList1()
{
	SLTNode* plist = NULL;
	SLPushFront(&plist, 3);
	SLPushFront(&plist, 2);
	SLPushFront(&plist, 1);
	SLPushBack(&plist, 4);
	SLPushBack(&plist, 5);
	SLPushBack(&plist, 6);

	SLTNode* pos = STFind(plist, 5);
	if (pos)
		SLInsertAfter(pos, 9);

	pos = STFind(plist, 4);
	if (pos)
		SLErase(&plist, pos);

	SLTPrint(plist);
}

3.4 链表练习

3.4.1 移除链表元素

203. 移除链表元素 - 力扣(LeetCode)icon-default.png?t=O83Ahttps://leetcode.cn/problems/remove-linked-list-elements/description/

struct ListNode* removeElements(struct ListNode* head, int val) 
{
	struct ListNode* cur = head;
    //prev初始化为NULL方便之后进行判断
	struct ListNode* prev = NULL;
	while (cur)
	{
		if (cur->val == val)
		{
			if (prev)
			{
				prev->next = cur->next;
				free(cur);
				cur = prev->next;
			}
			else
			{
				cur = head->next;
                //这里不能忘记释放head空间
				free(head);
				head = cur;
			}
		}
		else
		{
			prev = cur;
			cur = cur->next;
		}
	}
	return head;
}

3.4.2 反转链表

206. 反转链表 - 力扣(LeetCode)icon-default.png?t=O83Ahttps://leetcode.cn/problems/reverse-linked-list/description/

struct ListNode* reverseList(struct ListNode* head) 
{
	if (head == NULL)
		return NULL;
    
	struct ListNode* n1 = NULL;
	struct ListNode* n2 = head;
	struct ListNode* n3 = n2->next;

	while (n2)
	{
		n2->next = n1;
		//迭代
		n1 = n2;
		n2 = n3;
		if (n3)
			n3 = n3->next;
	}
	return n1;
}

3.4.3 链表的中间节点

876. 链表的中间结点 - 力扣(LeetCode)icon-default.png?t=O83Ahttps://leetcode.cn/problems/middle-of-the-linked-list/

struct ListNode* middleNode(struct ListNode* head)
{
	struct ListNode* slow = head;
	struct ListNode* fast = head;

    //这里一定同时满足两个都不为空,否则会出现空指针的访问错误
	while (fast && fast->next)
	{
		slow = slow->next;
		fast = fast->next->next;
	}
	return slow;
}

3.4.4 链表中倒数第k个节点

//fast走到最后一个节点,slow就是倒数第k个节点
//所以她们之间要相差k个节点
struct ListNode* FindKthToTail(struct ListNode* pListHead, int k)
{
	struct ListNode* fast, * slow = pListHead;
	while (k--)
	{
		if (fast == NULL)
			return NULL;
		fast = fast->next;
	}

	while (fast)
	{
		fast = fast->next;
		slow = slow->next;
	}
	return slow;
}

3.4.5 合并两个有序链表

21. 合并两个有序链表 - 力扣(LeetCode)icon-default.png?t=O83Ahttps://leetcode.cn/problems/merge-two-sorted-lists/description/

struct ListNode* mergeTwoLists(struct ListNode* list1, struct ListNode* list2) 
{
	if (list1 == NULL)
		return list2;
	if (list2 == NULL)
		return list1;

	struct ListNode* newhead = NULL;
	struct ListNode* tail = NULL;
	while (list1 && list2)
	{
		//谁小/相等就做新的头节点
		if (list1->val < list2->val)
		{
			if (newhead == NULL)
			{
				newhead = tail = list1;
			}
			else
			{
				tail->next = list1;
				tail = tail->next;
			}
			list1 = list1->next;
		}
		else
		{
			if (newhead == NULL)
			{
				newhead = tail = list2;
			}
			else
			{
				tail->next = list2;
				tail = tail->next;
			}
			list2 = list2->next;
		}
	}
	if (list1)
		tail->next = list1;
	if (list2)
		tail->next = list2;
	return newhead;
}

合并两个有序链表

3.4.6 链表分割

链表分割_牛客题霸_牛客网 (nowcoder.com)icon-default.png?t=O83Ahttps://www.nowcoder.com/practice/0e27e0b064de4eacac178676ef9c9d70?tpId=8&&tqId=11004&rp=2&ru=/activity/oj&qru=/ta/cracking-the-coding-interview/question-ranking

ListNode* partition(ListNode* pHead, int x)
{
	struct ListNode* lesshead, * lesstail, * greaterhead, * greatertail;
	lesshead = lesstail = (struct ListNode*)malloc(sizeof(struct ListNode));
	greaterhead = greatertail = (struct ListNode*)malloc(sizeof(struct ListNode));
	struct ListNode* cur = pHead;
	while (cur)
	{
		if (cur->val < x)
		{
			lesstail->next = cur;
			lesstail = lesstail->next;
		}
		else
		{
			greatertail->next = cur;
			greatertail = greatertail->next;
		}
		cur = cur->next;
	}
	lesstail->next = greaterhead->next;
	greatertail->next = NULL;

	pHead = lesshead->next;
	free(lesshead);
	free(greaterhead);
	return pHead;
}

3.4.7 链表的回文结构

链表的回文结构_牛客题霸_牛客网 (nowcoder.com)icon-default.png?t=O83Ahttps://www.nowcoder.com/practice/d281619e4b3e4a60a2cc66ea32855bfa?tpId=49&&tqId=29370&rp=1&ru=/activity/oj&qru=/ta/2016test/question-ranking

bool chkPalindrome(ListNode* A) 
{
	ListNode* mid = middleNode(A);
	ListNode* rmid = reverseList(mid);

	while (rmid)
	{
		if (rmid->val != A->val)
		{
			return false;
		}
		else
		{
			rmid = rmid->next;
			A = A->next;
		}
	}
	return true;
}

这道题用到了上面的找中间节点和翻转链表,直接复制即可。

3.4.8 相交链表

160. 相交链表 - 力扣(LeetCode)icon-default.png?t=O83Ahttps://leetcode.cn/problems/intersection-of-two-linked-lists/

struct ListNode* getIntersectionNode(struct ListNode* headA, struct ListNode* headB) 
{
	//先找到两个链表最后一个节点
	//顺便计算两个链表的长度
	struct ListNode* tailA = headA;
	struct ListNode* tailB = headB;
	int lenA = 1;
	int lenB = 1;
	while (tailA->next)
	{
		tailA = tailA->next;
		lenA++;
	}
	while (tailB->next)
	{
		tailB = tailB->next;
		lenB++;
	}
	
	//如果两个链表最后一个节点不同一定不相交
	if (tailA != tailB)
	{
		return NULL;
	}

	//求两个链表相差的节点个数,取绝对值
	int gap = abs(lenA - lenB);
	//先假设A比B长
	struct ListNode* longList = headA;
	struct ListNode* shortList = headB;
	//如果假设不对再改正
	if (lenA < lenB)
	{
		longList = headB;
		shortList = headA;
	}
	//先让长的链表走相差的节点个数
	while (gap--)
	{
		longList = longList->next;
	}
	//到这里两个链表之后的节点个数就相同了
	while (longList != shortList)
	{
		longList = longList->next;
		shortList = shortList->next;
	}
	//相同就到了相交的节点
	return longList;
}

3.4.9 环形链表

141. 环形链表 - 力扣(LeetCode)icon-default.png?t=O83Ahttps://leetcode.cn/problems/linked-list-cycle/description/

bool hasCycle(struct ListNode* head)
{
	struct ListNode* fast = head;
	struct ListNode* slow = head;
	while (fast && fast->next)
	{
		fast = fast->next->next;
		slow = slow->next;
		if (slow == fast)
			return true;
	}
	return false;
}

有两个问题:

  • 为什么快指针每次走两步,慢指针走一步可以相遇?

假设链表带环,两个指针最后都会进入环,快指针先进环,慢指针后进环。当慢指针刚进环时,可能就和快指针相遇了,最差情况下两个指针之间的距离刚好就是环的长度。此时,两个指针每移动一次,之间的距离就缩小一步,不会出现每次刚好是套圈的情况,因此:在满指针走到一圈之前,快指针肯定是可以追上慢指针的,即相遇。

  • 快指针一次走3步,走4步,...n步行吗

3.4.10 环形链表2

142. 环形链表 II - 力扣(LeetCode)icon-default.png?t=O83Ahttps://leetcode.cn/problems/linked-list-cycle-ii/

struct ListNode* detectCycle(struct ListNode* head)
{
	struct ListNode* slow = head;
	struct ListNode* fast = head;
	while (fast && fast->next)
	{
		slow = slow->next;
		fast = fast->next->next;
		if (slow == fast)
		{
			//求入口点
			struct ListNode* meet = slow;
			while (head != meet)
			{
				head = head->next;
				meet = meet->next;
			}
			return meet;
		}
	}
	return NULL;
}

3.4.11 随机链表的复制

138. 随机链表的复制 - 力扣(LeetCode)icon-default.png?t=O83Ahttps://leetcode.cn/problems/copy-list-with-random-pointer/description/

struct Node* copyRandomList(struct Node* head)
{
	//1.拷贝节点插入在原节点的后面
	struct Node* cur = head;
	while (cur)
	{
		struct Node* copy = (struct Node*)malloc(sizeof(struct Node));
		copy->val = cur->val;
		struct Node* next = cur->next;
		//插入
		cur->next = copy;
		copy->next = next;

		cur = next;
	}

	//2.控制拷贝节点的random
	cur = head;
	while (cur)
	{
		struct Node* copy = cur->next;

		if (cur->random == NULL)
		{
			copy->random = NULL;
		}
		else
		{
			copy->random = cur->random->next;
		}
		cur = copy->next;
	}

	struct Node* copyHead = NULL;
	struct Node* copyTail = NULL;
	cur = head;
	while (cur)
	{
		struct Node* copy = cur->next;
		struct Node* next = copy->next;

		//尾插
		if (copyTail == NULL)
		{
			copyHead = copyTail = copy;
		}
		else
		{
			copyTail->next = copy;
			copyTail = copyTail->next;
		}

		cur->next = next;
		cur = next;
	}
	return copyHead;
}

3.5 实现双向链表的代码

3.5.1 List.h

#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <stdbool.h>

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

LTNode* LTInit();
//打印
void LTPrint(LTNode* phead);

bool LTEmpty(LTNode* phead);
//尾插
void LTPushBack(LTNode* phead, LTDataType x);
//头插
void LTPushFront(LTNode* phead, LTDataType x);
//尾删
void LTPopBack(LTNode* phead);
//头删
void LTPopFront(LTNode* phead);
//查找
LTNode* LTFind(LTNode* phead, LTDataType x);
//在pos之前插入
void LTInsert(LTNode* pos, LTDataType x);
//删除pos位置的值
void LTErase(LTNode* pos);
//销毁
void LTDestroy(LTNode* phead);

3.5.2 List.c

#include "List.h"

LTNode* BuyLTNode(LTDataType x)
{
	LTNode* newnode = (LTNode*)malloc(sizeof(LTNode));
	if (newnode == NULL)
	{
		perror("malloc fail");
		return;
	}
	newnode->data = x;
	newnode->next = NULL;
	newnode->prev = NULL;
	return newnode;
}

//返回一个头节点,前驱和后继都指向自己
LTNode* LTInit()
{
	LTNode* phead = BuyLTNode(-1);
	phead->next = phead;
	phead->prev = phead;
	return phead;
}

void LTPrint(LTNode* phead)
{
	assert(phead);

	printf("guard<==>");
	LTNode* cur = phead->next;
	//cur==phead就表示链表已经走完了
	while (cur != phead)
	{
		printf("%d<==>", cur->data);
		cur = cur->next;
	}
	printf("\n");
}

bool LTEmpty(LTNode* phead)
{
	assert(phead);

	return phead->next == phead;
}

//尾插
void LTPushBack(LTNode* phead, LTDataType x)
{
	assert(phead);

	LTNode* tail = phead->prev;
	LTNode* newnode = BuyLTNode(x);

	tail->next = newnode;
	newnode->prev = tail;
	newnode->next = phead;
	phead->prev = newnode;

	//在我们写了在pos位置之前插入的代码之后就可以
	//LTInsert(phead, x);
}

//头插
void LTPushFront(LTNode* phead, LTDataType x)
{
	assert(phead);

	LTNode* first = phead->next;
	LTNode* newnode = BuyLTNode(x);

	phead->next = newnode;
	newnode->prev = phead;
	newnode->next = first;
	first->prev = newnode;

	//在我们写了在pos位置之前插入的代码之后就可以
	//LTInsert(phead->next, x);
}

//尾删
void LTPopBack(LTNode* phead)
{
	assert(phead);
	assert(!LTEmpty(phead));

	LTNode* tail = phead->prev;
	LTNode* tailprev = tail->prev;

	tailprev->next = phead;
	phead->prev = tailprev;
	free(tail);

	//在我们写了删除pos的位置的值的代码之后就可以
	//LTErase(phead->prev);
}

//头删
void LTPopFront(LTNode* phead)
{
	assert(phead);
	assert(!LTEmpty(phead));

	LTNode* first = phead->next;
	LTNode* second = first->next;

	phead->next = second;
	second->prev = phead;
	free(first);

	//在我们写了删除pos的位置的值的代码之后就可以
	//LTErase(phead->next);
}

LTNode* LTFind(LTNode* phead, LTDataType x)
{
	assert(phead);

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

//在pos之前插入
void LTInsert(LTNode* pos, LTDataType x)
{
	assert(pos);

	LTNode* prev = pos->prev;
	LTNode* newnode = BuyLTNode(x);

	prev->next = newnode;
	newnode->prev = prev;
	newnode->next = pos;
	pos->prev = newnode;
}

//删除pos位置的值
void LTErase(LTNode* pos)
{
	assert(pos);

	LTNode* prev = pos->prev;
	LTNode* next = pos->next;

	prev->next = next;
	next->prev = prev;
	free(pos);
}

void LTDestroy(LTNode* phead)
{
	assert(phead);

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

3.5.3 测试代码

void TestList1()
{
	LTNode* plist = LTInit();
	LTPushFront(plist, 3);
	LTPushFront(plist, 2);
	LTPushFront(plist, 1);
	LTPushBack(plist, 4);
	LTPushBack(plist, 5);
	LTPushBack(plist, 6);

	LTNode* pos = LTFind(plist, 3);
	if (pos)
	{
		LTInsert(pos, 4);
	}

	pos = LTFind(plist, 3);
	if (pos)
	{
		LTErase(pos);
	}

	LTPrint(plist);
	LTDestroy(plist);
	plist = NULL;
}

4、顺序表和链表的区别

  • 19
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值