数据结构(初阶)——顺序表、链表

一、顺序表

SeqList.h

#pragma once
#include
#include
#include
#include
#include
typedef int SLDataType;
// 顺序表的动态存储
typedef struct SeqList
{
	SLDataType* array; // 指向动态开辟的数组
	size_t size; // 有效数据个数
	size_t capicity; // 容量空间的大小
}SeqList;
SeqList SL;
// 基本增删查改接口
// 顺序表初始化
void SeqListInit(SeqList* psl);

// 检查空间,如果满了,进行增容
void CheckCapacity(SeqList* psl);

// 顺序表尾插
void SeqListPushBack(SeqList* psl, SLDataType x);

// 顺序表尾删
void SeqListPopBack(SeqList* psl);

// 顺序表头插
void SeqListPushFront(SeqList* psl, SLDataType x);

// 顺序表头删
void SeqListPopFront(SeqList* psl);

// 顺序表查找
int SeqListFind(SeqList* psl, SLDataType x);

// 顺序表在pos位置插入x
void SeqListInsert(SeqList* psl, size_t pos, SLDataType x);

// 顺序表删除pos位置的值
void SeqListErase(SeqList* psl, size_t pos);

// 顺序表销毁
void SeqListDestory(SeqList* psl);

// 顺序表打印
void SeqListPrint(SeqList* psl);

//顺序表修改
void SLModify(SeqList* psl, size_t pos, SLDataType x);

SeqList.c

#define _CRT_SECURE_NO_WARNINGS 

#include "SeqList.h"

// 顺序表初始化
void SeqListInit(SeqList* psl) {
	assert(psl);

	psl->capicity = 0;
	psl->size = 0;
	psl->array = NULL;
	

}

 //顺序表打印
void SeqListPrint(SeqList* psl) {
	assert(psl);
	for (int i = 0; i < psl->size; i++) {
		printf("%d ", psl->array[i]);
	}

}

// 顺序表头插
void SeqListPushFront(SeqList* psl, SLDataType x) {
	assert(psl);

	//检查空间是否足够
	 CheckCapacity(&psl);
	 int temp_size = psl->size;
	 while (temp_size > 0) {

		 psl->array[temp_size] = psl->array[temp_size-1];
		 temp_size--;

	 }
	 psl->array[0] = x;
	 psl->size++;

}

// 顺序表尾插
void SeqListPushBack(SeqList* psl, SLDataType x) {
	assert(psl);

	//检查空间是否足够
	CheckCapacity(&psl);

	psl->array[psl->size] = x;
	psl->size++;

}

// 顺序表销毁
void SeqListDestory(SeqList* psl) {

	assert(psl);
	free(psl->array);
	psl->array = NULL;
	psl->capicity = 0;
	psl->size = 0;

}
// 检查空间,如果满了,进行增容
void CheckCapacity(SeqList** psl) {

	if ((*psl)->capicity == 0) {
		SLDataType* temp = NULL;
		(*psl)->capicity = 4 * sizeof(SLDataType);
		temp = (SLDataType)malloc((*psl)->capicity);
		if (temp == NULL) {
			perror(CheckCapacity);
			return 0;
		}
		(*psl)->array = temp;

		return;
	}

	if ((*psl)->capicity <= (*psl)->size*sizeof(SLDataType)) {
		(*psl)->capicity *= 2;
		SLDataType* temp = NULL;
		//检查开辟是否成功
		temp = (SLDataType)realloc((*psl)->array,(*psl)->capicity);

		if (temp == NULL) {
			perror(CheckCapacity);
			return 0;
		}
		(*psl)->array = temp;
	}

}

// 顺序表尾删
void SeqListPopBack(SeqList* psl) {

	assert(psl);

	psl->size--;
}

// 顺序表头删
void SeqListPopFront(SeqList* psl) {
	assert(psl);

	int temp_size = 1;
	while (temp_size <= psl->size) {

		psl->array[temp_size-1] = psl->array[temp_size];
		temp_size++;

	}
	psl->size--;

}
// 顺序表查找
int SeqListFind(SeqList* psl, SLDataType x) {
	assert(psl);
	//找到返回下标
	for (int i = 0; i < psl->size; i++) {
		if (psl->array[i] == x) {

			return i;
		}
	}
	//没找到返回-1;
	return -1;
}


// 顺序表删除pos位置的值
void SeqListErase(SeqList* psl, size_t pos) {

	assert(psl);
	assert(pos < psl->size);
	if (pos >= psl->size) {
		printf("删除超出范围限制");
		return ;
	}

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

// 顺序表在pos位置插入x
void SeqListInsert(SeqList* psl, size_t pos, SLDataType x) {

	assert(psl);
	assert(pos <= psl->size);

	CheckCapacity(&psl);

	int temp = psl->size;
	while (temp > pos) {
		psl->array[temp] = psl->array[temp - 1];
		temp--;
	}
	psl->array[pos] = x;
	psl->size++;
}

//修改
void SLModify(SeqList* psl, size_t pos, SLDataType x) {

	assert(psl);
	assert(pos < psl->size);
	
	psl->array[pos] = x;

}

二.单链表

单链表.h

#pragma once
#include
#include
#include

typedef int SLTDateType;
typedef struct SListNode
{
	SLTDateType data;
	struct SListNode* next;
}SListNode;
// 动态申请一个结点
SListNode* BuySListNode(SLTDateType x);
// 单链表打印
void SListPrint(SListNode* plist);
// 单链表尾插
void SListPushBack(SListNode** pplist, SLTDateType x);
// 单链表的头插
void SListPushFront(SListNode** pplist, SLTDateType x);
// 单链表的尾删
void SListPopBack(SListNode** pplist);
// 单链表头删
void SListPopFront(SListNode** pplist);
// 单链表查找
SListNode* SListFind(SListNode* plist, SLTDateType x);
// 单链表在pos位置之后插入x
// 分析思考为什么不在pos位置之前插入?
void SListInsertAfter(SListNode* pos, SLTDateType x);
// 单链表 之前插入(难)
void SListInsertBefore(SListNode* pos, SLTDateType x);
// 单链表删除pos位置之后的值
// 分析思考为什么不删除pos位置?
void SListEraseAfter(SListNode* pos);
// 单链表销毁
void SLlistDestory(SListNode* phead);
//单链表删除当前位置
void SListErase(SListNode* pos);

单链表.c

#define _CRT_SECURE_NO_WARNINGS
#include"单链表.h"

SListNode* BuySListNode(SLTDateType x) {

	SListNode* temp = (SListNode*)malloc(sizeof(SListNode));

	if (temp == NULL) {
		perror("malloc fail");
		exit(-1);
	}
	SListNode* newnode = temp;
	temp = NULL;

	newnode->data = x;
	newnode->next = NULL;

	return newnode;
}

// 单链表打印
void SListPrint(SListNode* phead) {

	SListNode* cur = phead;

	while (cur) {
		printf("%d->", cur->data);
		cur = cur->next;
	}
	printf("NULL\n");

}

// 单链表的头插
void SListPushFront(SListNode** pphead, SLTDateType x) {   //改变一级指针就需要一级指针的地址,所以传入二级指针
	assert(pphead);
	SListNode* newnode = BuySListNode(x);

	newnode->next = *pphead;
	*pphead = newnode;

}

// 单链表尾插
void SListPushBack(SListNode** pphead, SLTDateType x) {
	assert(pphead);
	//单链表不为空
	if (* pphead == NULL ) {
		SListNode* next = BuySListNode(x);
		*pphead = next;
		

	}//单链表为空
	else {
		SListNode* cur = *pphead;
		while (cur->next != NULL) {
			cur = cur->next;
		}

		SListNode* next = BuySListNode(x);
		cur->next = next;
	}

}
// 单链表头删
void SListPopFront(SListNode** pphead) {
	
	assert(pphead);
	if (*pphead == NULL) {
		return;
	}
	SListNode* del = *pphead;
	*pphead = (*pphead)->next;
	free(del);
	del = NULL;

}

// 单链表的尾删
void SListPopBack(SListNode** pphead) {

	assert(pphead);
	if (*pphead == NULL) {
		return;
	}
	if ((*pphead)->next == NULL) {
		free(*pphead);
		*pphead = NULL;
		return;
	}
	SListNode* cur = *pphead;

	while (cur->next->next != NULL) {
		cur = cur->next;
	}
	free(cur->next);
	cur->next = NULL;


}
// 单链表查找
SListNode* SListFind(SListNode* pphead, SLTDateType x) {

	assert(pphead);
	SListNode* cur = pphead;

	while (cur) {
		if (x == cur->data) {
			printf("找到了\n");
			return cur;
		}
		else {
			cur = cur->next;
		}

	}
	return NULL;

}


//单链表在某位置之后插入
void SListInsertAfter(SListNode** pphead,SListNode* pos, SLTDateType x) {

	assert(pphead);

	if (*pphead == NULL) {
		SListPushFront(pphead, x);
		return;
	}
	
	SListNode* newcode =  BuySListNode(x);
	newcode->next = pos->next;//注意这两行的顺序
	pos->next = newcode;

}
// 单链表 之前插入(难)
void SListInsertBefore(SListNode** pphead, SListNode* pos, SLTDateType x) {

	assert(pos);
	assert(pphead);

	if (*pphead == NULL) {
		SListPushFront(pphead, x);
		return;
	}

	//方法一:找到前一项的next
	SListNode* cur = *pphead;
	if (pos == pphead) {
		SListPushFront(pphead, x);
		return;
	}
	while (cur->next != pos) {

		cur = cur->next;

		//找不到,说明pos传错了
		assert(cur);
	}
	SListNode* newcode = BuySListNode(x);
	newcode->next = pos;
	cur->next = newcode;
	//方法二 顺序法交换

	//SListNode* newcode = BuySListNode(pos->data);
	//pos->data = x;


}
// 单链表删除pos位置之后的值
// 分析思考为什么不删除pos位置?
void SListEraseAfter(SListNode** pphead, SListNode* pos) {

	assert(pphead);
	assert(pos);
	
	if (pos->next == NULL) {
		SListPopBack(pphead);
		return;
	}
	SListNode* cur = pos->next;
	pos->next = pos->next->next;

	free(cur);
}


//单链表删除当前位置
void SListErase(SListNode** pphead, SListNode* pos) {
	assert(pphead && pos);

	if (pos == *pphead) {
		SListPopFront(pphead);
		return;
	}

	SListNode* cur = pos->next->next;
	pos->data = pos->next->data;
	free(pos->next);
	pos->next = cur;

}


// 单链表销毁
void SLlistDestory(SListNode** pphead) {

	assert(pphead);
	SListNode* cur = pphead;
	SListNode* next = pphead;
	while (cur) {
		next = cur->next;
		free(cur);
		cur = next;
	}

	pphead = NULL;

}

三.带头双向循环链表

List.h

#pragma once
#include
#include
#include

typedef int LTDataType;
typedef struct ListNode
{
	LTDataType _data;
	struct ListNode* next;
	struct ListNode* prev;
}ListNode;
// 创建返回链表的头结点.
ListNode* ListCreate();
//双向链表创造一个新节点
ListNode* ListBuyNode(LTDataType x);
// 双向链表打印
void ListPrint(ListNode* plist);
// 双向链表尾插
void ListPushBack(ListNode* plist, LTDataType x);
// 双向链表头插
void ListPushFront(ListNode* plist, LTDataType x);
// 双向链表尾删
void ListPopBack(ListNode* plist);
// 双向链表头删
void ListPopFront(ListNode * plist);
// 双向链表查找
ListNode* ListFind(ListNode* plist, LTDataType x);
// 双向链表在pos的前面进行插入
void ListInsert(ListNode* pos, LTDataType x);
// 双向链表删除pos位置的结点
void ListErase(ListNode* pos);
// 双向链表销毁
void ListDestory(ListNode* plist);



List.c

#define _CRT_SECURE_NO_WARNINGS 
#include"List.h"

// 创建返回链表的头结点.
ListNode* ListCreate() {
	ListNode* ListGuard = (ListNode*)malloc(sizeof(ListNode));
	if (ListGuard == NULL) {
		perror("ListGuard malloc fai");
		exit(-1);
	}
	ListGuard->next = ListGuard;
	ListGuard->prev = ListGuard;
	return ListGuard;
}

// 双向链表打印
void ListPrint(ListNode* plist) {
	assert(plist);
	ListNode* head = plist;
	ListNode* cur = head->next;

	printf("head<=>");
	while (cur != head) {
		printf("%d<=>", cur->_data);
		cur = cur->next;
	}
	printf("\n");
}
//双向链表创造一个新节点
ListNode* ListBuyNode(LTDataType x) {
	ListNode* newNode = (ListNode*)malloc(sizeof(ListNode));
	if (newNode == NULL) {
		perror("newNode malloc fail");
		exit(-1);
	}
	newNode->next = NULL;
	newNode->prev = NULL;
	newNode->_data = x;
	return newNode;
}
// 双向链表尾插
void ListPushBack(ListNode* plist, LTDataType x) {
	assert(plist);
	ListNode* tail = plist->prev;

	ListNode* newNode = ListBuyNode(x);
	//节点四步走 注意语句顺序!
	newNode->next = plist;
	plist->prev = newNode;
	tail->next = newNode;
	newNode->prev = tail;

}
// 双向链表头插
void ListPushFront(ListNode* plist, LTDataType x) {
	assert(plist);
	ListNode* first = plist->next;

	ListNode* newNode = ListBuyNode(x);
	//节点四步走 注意语句顺序!
	first->prev = newNode;
	newNode->next = first;
	plist->next = newNode;
	newNode->prev = plist;

}
//检查双向列表是否为空
int CheckList(ListNode* plist) {
	assert(plist);
	//返回“真”为空 返回"假"为非空;
	return plist->next == plist->prev;
}
// 双向链表尾删
void ListPopBack(ListNode* plist) {
	assert(plist);
	assert(!CheckList(plist));
	ListNode* tail = plist->prev;

	//节点链接
	tail->prev->next = plist;
	plist->prev = tail->prev;
	//释放
	free(tail);
}
// 双向链表头删
void ListPopFront(ListNode* plist) {
	assert(plist);
	assert(!CheckList(plist));
	ListNode* first = plist->next;

	//节点链接
	plist->next = first->next;
	first->next->prev = plist;

	//释放
	free(first);
}
// 双向链表查找
ListNode* ListFind(ListNode* plist, LTDataType x) {
	assert(plist);
	if (CheckList(plist)) {
		printf("链表为NULL,无法查找有效数据\n");
		exit(-1);
	}
	ListNode* cur = plist->next;
	while (cur != plist) {
		if (cur->_data == x) {
			printf("找到啦\n");
			return cur;
		}
		cur = cur->next;
	}
	return NULL;
}
// 双向链表在pos的前面进行插入
void ListInsert(ListNode* pos, LTDataType x) {
	assert(pos);
	ListNode* first = pos->prev;
	ListNode* second = pos;
	ListNode* newNode = ListBuyNode(x);

	newNode->next = second;
	second->prev = newNode;
	first->next = newNode;
	newNode->prev = first;

}
// 双向链表删除pos位置的结点
void ListErase(ListNode* pos) {
	assert(pos);
	ListNode* first = pos->prev;
	ListNode* del = pos;
	ListNode* second = pos->next;

	first->next = second;
	second->prev = first;
	free(del);
}
// 双向链表销毁
void ListDestory(ListNode* plist) {
	assert(plist);
	ListNode* cur = plist->next;
	ListNode* next = NULL;
	plist->next = NULL;
	while (cur != NULL) {
		next = cur->next;
		free(cur);
		cur = next;
	}
	printf("销毁成功\n");
}

四、顺序表、链表 oj

 顺序表

1. 原地移除数组中所有的元素 val ,要求时间复杂度为 O(N) ,空间复杂度为 O(1)
力扣
2. 删除排序数组中的重复项。
力扣
3. 合并两个有序数组。

力扣

链表

1. 删除链表中等于给定值 val 的所有结点  力扣
2. 反转一个单链表。
力扣
3. 给定一个带有头结点 head 的非空单链表,返回链表的中间结点。如果有两个中间结点,则
返回第二个中间结点。
力扣
4. 输入一个链表,输出该链表中倒数第 k 个结点。
链表中倒数第k个结点_牛客题霸_牛客网
5. 将两个有序链表合并为一个新的有序链表并返回。新链表是通过拼接给定的两个链表的所有
结点组成的。
力扣
6. 编写代码,以给定值 x 为基准将链表分割成两部分,所有小于 x 的结点排在大于或等于 x 的结
点之前 。 链表分割_牛客题霸_牛客网
7. 链表的回文结构。 链表的回文结构_牛客题霸_牛客网
8. 输入两个链表,找出它们的第一个公共结点。 力扣
9. 给定一个链表,判断链表中是否有环。 力扣
【思路】
快慢指针,即慢指针一次走一步,快指针一次走两步,两个指针从链表其实位置开始运行,
如果链表
带环则一定会在环中相遇,否则快指针率先走到链表的末尾。比如:陪女朋友到操作跑步减
肥。
10. 给定一个链表,返回链表开始入环的第一个结点。 如果链表无环,则返回 NULL 力扣
11. 给定一个链表,每个结点包含一个额外增加的随机指针,该指针可以指向链表中的任何结点
或空结点。
要求返回这个链表的深度拷贝。 力扣
12. 其他 。 ps :链表的题当前因为难度及知识面等等原因还不适合我们当前学习,以后大家自己
下去以后 力扣 牛客网 - 找工作神器|笔试题库|面试经验|实习招聘内推,求职就业一站解决_牛客网
顺序表优点:
1.尾插、尾删效率高。
2.可以随机访问。.
(了解)3.cpu高速缓存命中率更高(相比链表)
顺序表缺点:
1.头部中部插入效率低——O(N)
2.扩容 ——性能消耗,空间浪费
链表优点:
1.任意插入删除都很方便
2.扩容——不存在空间浪费(按需申请空间)
链表缺点:
1.扩容——每次都要申请malloc浪费了时间
2.不能随机访问
数据结构(初阶)——顺序表、链表_第1张图片

 

你可能感兴趣的:(链表,数据结构)