链表和顺序表

顺序表:

顺序表(Sequential List)是一种使用顺序存储结构实现的线性表,它将线性表的元素存储在一段连续的存储单元中。顺序表的特点是元素在内存中的存储位置是连续的,通过元素在存储单元中的相对位置来表示元素之间的逻辑顺序。

顺序表通常由以下几个要素组成:
1. 数据存储区:顺序表中用来存储元素的一段连续存储空间,通常使用数组来实现。
2. 表长度:记录当前顺序表中元素的个数。
3. 最大容量:记录顺序表所分配的存储空间的最大容量,防止数组越界。

typedef struct Sqlist
{
	Sqlist* _data;
	int _size;
	int _capacity;
}Sqlist;

 构造函数与析构函数

Sqlist::Sqlist(int capacity = 4)
{
	_capacity = capacity;
	Datatype* temp = (Datatype*)malloc(sizeof(Datatype) * _capacity);
	if (temp == nullptr)
	{
		perror("malloc");
		exit(-1);
	}
	_data = temp;

	_size = 0;
}

Sqlist::~Sqlist()
{
	free(_data);
	_data = nullptr;
	_capacity = _size = 0;

}

 可以理解为初始化函数与内存释放

插入与扩容


void Sqlist::Newcapacity() {
	_capacity *= 2;
	Datatype* newData = (Datatype*)realloc(_data, sizeof(Datatype) * _capacity);
	if (newData == nullptr) {
		perror("realloc");
		exit(-1);
	}

	else {
		_data = newData;
	}
}


void Sqlist::Insert(Datatype data)
{
	if (_size == _capacity)//判断当前线性表是否已满
	{
		Newcapacity();//更新线性表大小
	}

	_data[_size++] = data;

}

后置++ :先插入 数据再更新_size的大小

查询

遍历对象数组

int Sqlist::seek(Datatype data)
{
	for (int i = 0; i < _size; i++)
	{
		if (_data[i] == data)
		{
			return i;
		}
	}

	cout << "false";
	return -1;
}

返回值是数组索引,我们定义如果返回-1就是没有找到 

删除更改

void Sqlist::Erase(int index) {
	assert(index >= 0 && index < _size); 

	// 将指定位置后面的元素向前移动一个位置覆盖被删除的元素
	for (int i = index; i < _size - 1; ++i) {
		_data[i] = _data[i + 1];
	}

	--_size; 
}

void Sqlist::Alter(int index, Datatype newData) {
	assert(index >= 0 && index < _size); // 确保索引合法

	_data[index] = newData; // 将指定位置的元素替换为新的数据
}

我这里将查询与删除更改配合起来使用,直接调用查询的索引。对于头插头删的代码,就是在数组中删除,数据,这正是顺序表的弊端,需要不断移动数组中的元素。 9

顺序表的优点包括:
1. 随机访问:由于元素在内存中是连续存储的,因此可以通过下标直接访问任意位置的元素,实现了常数时间的随机访问。
2. 存储密度高:不需要额外的指针存储元素之间的关系,因此存储密度高,节省了存储空间。

顺序表的缺点包括:
1. 插入和删除操作效率低:在顺序表中进行插入和删除操作需要移动大量元素,时间复杂度为O(n),效率较低。
2. 空间浪费:顺序表需要预先分配一定大小的存储空间,可能会导致存储空间的浪费。
3. 容量固定:顺序表的容量在创建时确定,不能动态扩容,当存储空间不足时需要重新分配空间,可能导致数据复制和性能损耗。

顺序表常见的应用场景包括需要频繁进行随机访问,且元素个数相对稳定的情况下,如静态查找表、稠密矩阵等。在编程中,顺序表的实现比较简单,常被用来实现数组和列表等数据结构。

链表:

链表(Linked List)是一种常见的数据结构,它由一系列节点(Node)组成,每个节点包含两部分:数据域(Data)和指针域(Pointer)。数据域用来存储数据,指针域用来指向下一个节点,这样将所有节点按顺序连接起来就形成了链表。链表中的第一个节点称为头节点,最后一个节点的指针域通常指向空(nullptr 或 NULL),表示链表的结束。

链表可以分为单向链表、双向链表和循环链表等不同类型,根据指针域的不同连接方式。常见的有单向链表和双向链表:

1. 单向链表(Singly Linked List):每个节点只有一个指针域,指向其后继节点。
2. 双向链表(Doubly Linked List):每个节点有两个指针域,一个指向其前驱节点,一个指向其后继节点。我这里以单链表为例子

// 定义链表节点结构体
struct ListNode {
    DateType _data;
    ListNode* _next;
};

 由于C++构造函数会直接将头节点给赋值甚至我们不处理还是随机值,所以,我这里使用一个结构体代表链表本体,用List类储存他的头跟尾(插入的时候减少循环)

 ListNode* _head;
 ListNode* _tail;

构造函数与析构函数

List::List()
{
	_tail = _head = nullptr;
}

List::~List()
{
	ListNode* cur = _head;

	while (cur != nullptr)
	{
		ListNode* temp = cur->_next;
		free(cur);
		cur = temp;
	}
	_head = nullptr;
	_tail = nullptr;

}

 节点创建

ListNode* List::create(DateType data)
{
	ListNode* temp = (ListNode*)malloc(sizeof(List));
	if (temp == nullptr)
	{
		perror("malloc");
		exit(-1);
	}
	temp->_data = data;
	temp->_next = nullptr;

	return temp;

}

插入节点时,我们需要节点分配空间,直接封装成函数,省事

 头,尾插入

void List::Pushback(DateType data)
{
	if (_head == nullptr)
	{
		_head = create(data);
		_tail = _head;
		return;
	}

	ListNode* cur = create(data);

	_tail->_next = cur;
	_tail = cur;
}

void List::Pushfront(DateType data)
{

	ListNode* temp = create(data);
	if (_head == nullptr)
	{
		_head = temp;
		_tail = _head;
		return;
	}
	
	temp = _head->_next;
	_head = temp;

	if (_head = _tail)
	{
		_tail = temp->_next;
	}
}

包括头插,尾插,这时候我们保存尾节点就起到作用了,这里也需要注意头插的特殊情况 

查找

ListNode* List::Seek(int target) {
	ListNode* current = _head;  // 从链表头节点开始遍历
	while (current != nullptr) {
		if (current->_data == target) {
			return current;  // 找到节点,返回节点指针
		}
		current = current->_next;  // 移动到下一个节点
	}
	return nullptr;  // 没有找到节点,返回空指针
}

  插入

void List::Insert(ListNode* node, DateType data)
{
	ListNode* cur = _head;
	while (cur != node)
	{
		cur = cur->_next;
	}

	ListNode* temp = create(data);

	temp->_next = cur->_next;
	cur->_next = temp;

}

 删除

void List::Remove(ListNode* node)
{
	ListNode* cur = _head;
	ListNode* tail = cur;
	while (cur != node)
	{
		tail = cur;
		cur = cur->_next;
	}
	tail->_next = cur->_next;
	free(cur);

	cur = nullptr;

}

注意要先进行插入节点与后续节点的链接 

链表的优点包括:
- 插入和删除操作高效:在链表中插入或删除一个节点的时间复杂度为 O(1),只需改变节点的指针,不需要移动其他节点。
- 空间利用灵活:链表的节点在内存中分布不连续,可以根据实际需求动态地分配和释放内存。

链表的缺点包括:
- 随机访问效率低:由于链表中的元素不是连续存储的,因此不能像数组那样通过下标直接访问元素,需要从头节点开始遍历,时间复杂度为 O(n)。
- 需要额外的指针空间:链表中的每个节点都需要额外的指针来存储后继节点的地址,占用了额外的空间。

链表常被用于实现其他数据结构,如栈、队列和哈希表等,也常用于解决与数据结构相关的问题,如反转链表、合并链表等。

代码:zjsnh (github.com)

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值