双向链表的简单实现

news2024/12/26 11:56:46

目录

1. 双向链表的结构

2.双向链表的实现

2.1 初始化

2.2 增

2.2.1 尾插

​编辑

2.2.2 头插

3.删

2.3.1 尾删

2.3.2 头删

4. 找

5.任意位置插入

5.1 任意位置前插入

​编辑

5.2 任意位置后插入

​编辑

6. 任意位置删除

​编辑

7. 改

8. 链表的销毁

3. 顺序表和双向链表的优缺点分析

4.完整代码


1. 双向链表的结构

这是带头双向链表

注意:这里的“带头”和之前的文章中单链表和顺序表说的“头节点”是两个概念,实际前面的在单链
表阶段称呼不严 谨,但是更好的理解就直接称为单链表的头节点。
带头链表里的头节点,实际为“哨兵位”,哨兵位节点不存储任何有效元素,只是站在这⾥“放哨
的”
“哨兵位”存在的意义:
遍历循环链表避免死循环。

2.双向链表的实现

首先来定义节点的结构

typedef int typedata;

struct ListNode
{
	typedata val;
	struct ListNode* prev;//后节点
	struct ListNode* next;//前节点
};

typedef struct ListNode LNO;

2.1 初始化

在前面的文章中,一开始拿到的单链表多为空

双链表为空时,此时只剩下一个头节点

那么在初始化的时候,是否可以将哨兵位指向为空呢

此时看上文中的图,可以知道是不行的,这样他并没有循环起来

那么应该让他指向自身,使他自循环

那么干脆让每次申请的节点都先自循环

//申请节点
LNO* BuyNode(typedata X)
{
	LNO* newnode = (LNO*)malloc(sizeof(LNO));
	if (!newnode)
	{
		perror("malloc fail!");
		exit(1);
	}
	newnode->val = X;
	newnode->prev = newnode;
	newnode->next = newnode;//自循环
	return newnode;
}

LNOInit()
{
	LNO* pphead = BuyNode(-1);
	return pphead;
}//初始化链表

测试一下头节点初始化是否符合预期

#include"List.h"

void test01()
{
	LNO* plist = LNOInit();
}
int main()
{
	test01();
	return 0;
}

可以看到是符合预期的

接下来要实现的就是增删查改

2.2 增

在进行增的操作时

要注意:哨兵位的节点不能删除,也不能被改变

所以,为了避免修改哨兵位,在函数传参中我们采用一级指针形式

2.2.1 尾插

 在双向链表的末尾插入数据

那么首先,得找到双向链表的末尾

那么因为这里是循环链表肯定不能像之前的单链表那样通过判空来找尾节点

从图中不难发现尾节点就是plist->prev

进行数据的插入时,需要将原尾节点的next指向插入的节点,

新插入节点的prev指向原尾节点,next指针指向头节点

头节点的prev指针指向插入的节点

void LNOinsertback(LNO* phead, typedata X)
{
	LNO* newtail = BuyNode(X);
	LNO* ptail = phead->prev;
	ptail->next = newtail;//原节点指向新的尾节点
	phead->prev = newtail;//哨兵位的prev指向新节点
	newtail->prev = ptail;
	newtail->next = phead;
}

定义一个打印函数方便测试

打印双向链表数据的前提是这个双向链表中要有数据才行

从哨兵位后一个节点开始才是链表的有效数据,所以从哨兵位后一个节点开始打印

一次遍历下去打印完后再回到哨兵位就不用打印了


void printLNO(LNO* phead)
{
	assert(phead && phead->next);
	LNO* pres = phead->next;
	while (pres != phead)
	{
		printf("%d->", pres->val);
		pres = pres->next;
	}
}

测试一下

void test02()
{
	LNO* plist = LNOInit();
	LNOinsertback(plist, 1);
	printLNO(plist);

	LNOinsertback(plist, 2);
	printLNO(plist);

	LNOinsertback(plist, 3);
	printLNO(plist);

	LNOinsertback(plist, 4);
	printLNO(plist);

}
int main()
{
	test02();
	return 0;
}

可以看到是符合预期的

2.2.2 头插

 这里需要注意的是这个头插中的头指的是哪个节点

如果是头节点

此时,让尾节点的next指向插入的节点,然后让插入的节点的next指向plist,prev指向尾节点

但是,这种插入方法不就是尾插吗

所以这里的“头”肯定不是指的头节点

而应该是第一个有效数据所在的节点前插入

让新插入节点的next指向原首个有效数据所在的节点,prev指向plist

原首个有效数据所在的节点的prev指向新插入的节点

让plist的next指向新插入的节点

void LNOinsertfront(LNO* phead, typedata X)
{
	LNO* newhead = BuyNode(X);
	LNO* pcur = phead->next;
	newhead->next = pcur;
	newhead->prev = phead;//让新插入节点的next指向原首个有效数据所在的节点,prev指向头节点
	pcur->prev = newhead; //原首个有效数据所在的节点的prev指向新插入的节点
	phead->next = newhead;//让头节点的next指向新插入的节点
}

测试一下

void test03()
{
	LNO* plist = LNOInit();
	LNOinsertfront(plist, 1);
	printLNO(plist);

	LNOinsertfront(plist, 2);
	printLNO(plist);

	LNOinsertfront(plist, 3);
	printLNO(plist);

	LNOinsertfront(plist, 4);
	printLNO(plist);

}
int main()
{
	test03();
	return 0;
}

测试一下

是可行的

3.删

2.3.1 尾删

删除尾节点,即删除plist->prev

删除后将原尾节点前一个节点的next指针指向plist

plist->prev指向原尾节点前一个节点

void LNOdelback(LNO* phead)
{
	assert(phead && phead->next);
	LNO* pcur = phead;
	LNO* del = pcur->prev;//原尾节点
	LNO* newtail = del->prev;//新的尾节点
	newtail->next = phead;//原尾节点前一个节点的next指针指向头节点
	pcur->prev = newtail; //头节点的prev指向原尾节点前一个节点
	free(del);
	del = NULL;
}

测试一下

void test04()
{
	LNO* plist = LNOInit();
	LNOinsertfront(plist, 1);
	printLNO(plist);

	LNOinsertfront(plist, 2);
	printLNO(plist);

	LNOinsertfront(plist, 3);
	printLNO(plist);

	LNOinsertfront(plist, 4);
	printLNO(plist);

	LNOdelback(plist);
	printLNO(plist);

	LNOdelback(plist);
	printLNO(plist);

	LNOdelback(plist);
	printLNO(plist);

	LNOdelback(plist);
	printLNO(plist);

}
int main()
{
	test04();
	return 0;
}

可以看到是可行的

2.3.2 头删

删除第一个有效数据节点,即plist->next

删除后第二个有效数据节点成为新首节点,新首节点的prev指向plist

plist->next指向新首节点

void LNOdelfront(LNO* phead)
{
	assert(phead && phead->next);
	LNO* pcur = phead;
	LNO* del = phead->next;
	LNO* newhead = del->next;//新首节点
	newhead->prev = pcur;//新首节点的prev指向头节点
	pcur->next = newhead;//头节点的next指向新首节点
	free(del);
	del = NULL;
}

测试一下

void test05()
{
	LNO* plist = LNOInit();
	LNOinsertfront(plist, 1);
	printLNO(plist);

	LNOinsertfront(plist, 2);
	printLNO(plist);

	LNOinsertfront(plist, 3);
	printLNO(plist);

	LNOinsertfront(plist, 4);
	printLNO(plist);

	LNOdelfront(plist);
	printLNO(plist);

	LNOdelfront(plist);
	printLNO(plist);

	LNOdelfront(plist);
	printLNO(plist);

	LNOdelfront(plist);
	printLNO(plist);

}
int main()
{
	test05();
	return 0;
}

是可行的

4. 找

查找某个数据是否存在表中,找到了返回对应节点的地址,找不到返回NULL

LNO* Find(LNO*phead,typedata X)
{
	LNO* pcur = phead->next;
	while (pcur != phead)
	{
		if (pcur->val == X)
		{
			return pcur;
		}
		pcur = pcur->next;
	}
	return NULL;
}

5.任意位置插入

要用到查找方法,所以放在这里说

5.1 任意位置前插入

使用查找方法查找要插入的位置pos

插入节点newnode

newnode的next指向pos,prev指向pos的前一个节点

让pos前一个节点的next指向newnode,pos的prev指向这个newnode

//任意位置前插入
void LNOinsertbefore(typedata X, LNO* pos)
{
	assert(pos);
	LNO* newnode = BuyNode(X);
	LNO* temp = pos->prev;
	newnode->next = pos;
	newnode->prev = temp;//newnode的next指向pos,prev指向pos的前一个节点
	temp->next = newnode;
	pos->prev = newnode;//让pos前一个节点的next指向newnode,pos的prev指向这个newnode
}

测试一下

void test06()
{
	LNO* plist = LNOInit();
	LNOinsertfront(plist, 1);
	printLNO(plist);

	LNOinsertfront(plist, 2);
	printLNO(plist);

	LNOinsertfront(plist, 3);
	printLNO(plist);

	LNOinsertfront(plist, 4);
	printLNO(plist);

	LNO* find = Find(plist, 4);
	LNOinsertbefore( 5, find);
	printLNO(plist);

	find = Find(plist, 1);
	LNOinsertbefore(6, find);
	printLNO(plist);

	find = Find(plist, 3);
	LNOinsertbefore(7, find);
	printLNO(plist);
}
int main()
{
	test06();
	return 0;
}

符合预期

5.2 任意位置后插入

使用查找方法查找要插入的位置pos

插入节点newnode

newnode的prev指向pos,next指向pos的后一个节点

让pos的next指向newnode,pos后一个节点的prev指向这个newnode

//任意位置后插入
void LNOinsertafter(typedata X, LNO* pos)
{
	assert(pos);
	LNO* newnode = BuyNode(X);
	LNO* temp = pos->next;
	newnode->next = temp;
	newnode->prev = pos; //newnode的prev指向pos, next指向pos的后一个节点
	temp->prev = newnode;
	pos->next = newnode;//让pos的next指向newnode,pos后一个节点的prev指向这个newnode
}

测试一下

void test07()
{
	LNO* plist = LNOInit();
	LNOinsertfront(plist, 1);
	printLNO(plist);

	LNOinsertfront(plist, 2);
	printLNO(plist);

	LNOinsertfront(plist, 3);
	printLNO(plist);

	LNOinsertfront(plist, 4);
	printLNO(plist);

	LNO* find = Find(plist, 4);
	LNOinsertafter(5, find);
	printLNO(plist);


    find = Find(plist, 1);
	LNOinsertafter(6, find);
	printLNO(plist);


	 find = Find(plist, 3);
	LNOinsertafter(7, find);
	printLNO(plist);
}
int main()
{
	test07();
	return 0;
}

是可行的

6. 任意位置删除

要用到查找方法,所以放在这里说

先通过查找函数找到要删除的节点del

del前的节点的next指向del后节点

del后节点的prev指向del前节点

void LNOdel(LNO* del)
{
	LNO* temp1 = del->prev;//前节点
	LNO* temp2 = del->next;//后节点
	temp1->next = temp2;
	temp2->prev = temp1;//del前的节点的next指向del后节点del后节点的prev指向del前节点
	free(del);
	del = NULL;
}

测试

void test08()
{
	LNO* plist = LNOInit();
	LNOinsertfront(plist, 1);
	printLNO(plist);

	LNOinsertfront(plist, 2);
	printLNO(plist);

	LNOinsertfront(plist, 3);
	printLNO(plist);

	LNOinsertfront(plist, 4);
	printLNO(plist);

	LNOinsertfront(plist, 5);
	printLNO(plist);

	LNO* find = Find(plist, 5);
	LNOdel(find);
	printLNO(plist);

    find = Find(plist, 3);
	LNOdel(find);
	printLNO(plist);

	find = Find(plist, 1);
	LNOdel(find);
	printLNO(plist);

}

int main()
{
	test08();
	return 0;
}

可行

7. 改

使用查找方法查找要修改的位置pos

把对应的节点值修改就行了

void LNOmodify(LNO* pos, typedata X)
{
	pos->val = X;
}

void test09()
{
	LNO* plist = LNOInit();
	LNOinsertback(plist, 1);
	printLNO(plist);

	LNOinsertback(plist, 2);
	printLNO(plist);

	LNOinsertback(plist, 3);
	printLNO(plist);

	LNOinsertback(plist, 4);
	printLNO(plist);

	LNO* find = Find(plist, 4);
	LNOmodify(find, 5);
	printLNO(plist);

	find = Find(plist, 3);
	LNOmodify(find, 6);
	printLNO(plist);

	find = Find(plist, 2);
	LNOmodify(find, 7);
	printLNO(plist);

	find = Find(plist, 1);
	LNOmodify(find, 8);
	printLNO(plist);
}

int main()
{
	test09();
	return 0;
}

8. 链表的销毁

因为是动态申请过来的空间,不使用后要将他释放

//链表的销毁
void LNOdestory(LNO* phead)
{
	LNO* pcur = phead->next;
	while (pcur != phead)
	{
		LNO* save = pcur->next;
		LNOdel(pcur);
		pcur = save;//挨个挨个销毁
	}
	free(phead);
	phead = NULL;
}

测试


void test10()
{
	LNO* plist = LNOInit();
	LNOinsertback(plist, 1);
	printLNO(plist);

	LNOinsertback(plist, 2);
	printLNO(plist);

	LNOinsertback(plist, 3);
	printLNO(plist);

	LNOinsertback(plist, 4);
	printLNO(plist);

	LNOdestory(plist);
	plist = NULL;
}

int main()
{
	test10();
	return 0;
}

通过调试看出,这里除了plist,都释放了

手动给plist置空

LNOdelLNOdestory为什么不传二级指针

前文的函数中形参都是采用的一级指针

为了保持接口的一致性才传的一级,存在的问题就是形参phead释放置空后,实参plist不会置空,

因此要手动置空

更严谨来说在前文中任意位置删除的测试中,每删除一个位置都需要将find置空

void test08()
{
	LNO* plist = LNOInit();
	LNOinsertfront(plist, 1);
	printLNO(plist);

	LNOinsertfront(plist, 2);
	printLNO(plist);

	LNOinsertfront(plist, 3);
	printLNO(plist);

	LNOinsertfront(plist, 4);
	printLNO(plist);

	LNOinsertfront(plist, 5);
	printLNO(plist);

	LNO* find = Find(plist, 5);
	LNOdel(find);
	find = NULL;
	printLNO(plist);

    find = Find(plist, 3);
	LNOdel(find);
	find = NULL;
	printLNO(plist);

	find = Find(plist, 1);
	LNOdel(find);
	find = NULL;
	printLNO(plist);

}

3. 顺序表和双向链表的优缺点分析

不同点顺序表单链表
存储空间上物理上连续逻辑上连续,物理上不一定连续
随机访问支持O(1)不支持:O(N)
 任意位置插入或删除可能需要搬移元素,效率低O(N)只需修改指针指向
插入动态顺序表,空间不够是需要扩容没有容量的概念
应用场景元素高效储存+频繁访问任意位置插入或删除

4.完整代码

//List.h
#pragma once
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
//定义双向链表节点的结构

typedef int typedata;
struct ListNode
{
	typedata val;
	struct ListNode* prev;//后节点
	struct ListNode* next;//前节点
};

typedef struct ListNode LNO;


LNO* BuyNode(typedata X);//申请节点

LNO* LNOInit();//初始化

void printLNO(LNO* phead);//打印函数

void LNOinsertback(LNO* phead,typedata X);//尾插

void LNOinsertfront(LNO* phead,typedata X);//头插

void LNOdelback(LNO* phead);//尾删

void LNOdelfront(LNO* phead);//头删

LNO* Find(LNO*phead,typedata X);//查找

void LNOinsertbefore(typedata X, LNO* pos);//任意位置前插入

void LNOinsertafter(typedata X, LNO* pos);//任意位置后插入

void LNOdel(LNO* pos);//任意位置删除

void LNOmodify(LNO* pos, typedata X);//修改

void LNOdestory(LNO* phead);//链表的销毁

//List.c
#include "List.h"

//申请节点
LNO* BuyNode(typedata X)
{
	LNO* newnode = (LNO*)malloc(sizeof(LNO));
	if (!newnode)
	{
		perror("malloc fail!");
		exit(1);
	}
	newnode->val = X;
	newnode->prev = newnode;
	newnode->next = newnode;//自循环
	return newnode;
}

LNO*LNOInit()
{
	LNO* pphead = BuyNode(-1);
	return pphead;
}//初始化链表

void printLNO(LNO* phead)
{
	assert(phead && phead->next);
	LNO* pres = phead->next;
	while (pres != phead)
	{
		printf("%d->", pres->val);
		pres = pres->next;
	}
	printf("\n");
}


void LNOinsertback(LNO* phead, typedata X)
{
	LNO* newtail = BuyNode(X);
	LNO* ptail = phead->prev;
	ptail->next = newtail;//原节点指向新的尾节点
	phead->prev = newtail;//哨兵位的prev指向新节点
	newtail->prev = ptail;
	newtail->next = phead;//新插入节点的prev指向原尾节点,next指针指向头节点
}//尾插

void LNOinsertfront(LNO* phead, typedata X)
{
	LNO* newhead = BuyNode(X);
	LNO* pcur = phead->next;
	newhead->next = pcur;
	newhead->prev = phead;//让新插入节点的next指向原首个有效数据所在的节点,prev指向头节点
	pcur->prev = newhead; //原首个有效数据所在的节点的prev指向新插入的节点
	phead->next = newhead;//让头节点的next指向新插入的节点
}//头插

//尾删
void LNOdelback(LNO* phead)
{
	assert(phead && phead->next);
	LNO* pcur = phead;
	LNO* del = pcur->prev;//原尾节点
	LNO* newtail = del->prev;//新的尾节点
	newtail->next = phead;//原尾节点前一个节点的next指针指向头节点
	pcur->prev = newtail; //头节点的prev指向原尾节点前一个节点
	free(del);
	del = NULL;
}

//头删
void LNOdelfront(LNO* phead)
{
	assert(phead && phead->next);
	LNO* pcur = phead;
	LNO* del = phead->next;
	LNO* newhead = del->next;//新首节点
	newhead->prev = pcur;//新首节点的prev指向头节点
	pcur->next = newhead;//头节点的next指向新首节点
	free(del);
	del = NULL;
}

LNO* Find(LNO*phead,typedata X)
{
	LNO* pcur = phead->next;
	while (pcur != phead)
	{
		if (pcur->val == X)
		{
			return pcur;
		}
		pcur = pcur->next;
	}
	return NULL;
}

//任意位置删除
void LNOdel(LNO* del)
{
	LNO* temp1 = del->prev;//前节点
	LNO* temp2 = del->next;//后节点
	temp1->next = temp2;
	temp2->prev = temp1;//del前的节点的next指向del后节点del后节点的prev指向del前节点
	free(del);
	del = NULL;
}

//任意位置前插入
void LNOinsertbefore(typedata X, LNO* pos)
{
	assert(pos);
	LNO* newnode = BuyNode(X);
	LNO* temp = pos->prev;
	newnode->next = pos;
	newnode->prev = temp;//newnode的next指向pos,prev指向pos的前一个节点
	temp->next = newnode;
	pos->prev = newnode;//让pos前一个节点的next指向newnode,pos的prev指向这个newnode
}

//任意位置后插入
void LNOinsertafter(typedata X, LNO* pos)
{
	assert(pos);
	LNO* newnode = BuyNode(X);
	LNO* temp = pos->next;
	newnode->next = temp;
	newnode->prev = pos; //newnode的prev指向pos, next指向pos的后一个节点
	temp->prev = newnode;
	pos->next = newnode;//让pos的next指向newnode,pos后一个节点的prev指向这个newnode
}

//修改
void LNOmodify(LNO* pos, typedata X)
{
	pos->val = X;
}

//链表的销毁
void LNOdestory(LNO* phead)
{
	LNO* pcur = phead->next;
	while (pcur != phead)
	{
		LNO* save = pcur->next;
		LNOdel(pcur);
		pcur = save;//挨个挨个销毁
	}
	free(phead);
	phead = NULL;
}

//test.c
#include"List.h"

void test01()
{
	LNO* plist = LNOInit();
}

void test02()
{
	LNO* plist = LNOInit();
	LNOinsertback(plist, 1);
	printLNO(plist);

	LNOinsertback(plist, 2);
	printLNO(plist);

	LNOinsertback(plist, 3);
	printLNO(plist);

	LNOinsertback(plist, 4);
	printLNO(plist);
}

void test03()
{
	LNO* plist = LNOInit();
	LNOinsertfront(plist, 1);
	printLNO(plist);

	LNOinsertfront(plist, 2);
	printLNO(plist);

	LNOinsertfront(plist, 3);
	printLNO(plist);

	LNOinsertfront(plist, 4);
	printLNO(plist);

}

void test04()
{
	LNO* plist = LNOInit();
	LNOinsertfront(plist, 1);
	printLNO(plist);

	LNOinsertfront(plist, 2);
	printLNO(plist);

	LNOinsertfront(plist, 3);
	printLNO(plist);

	LNOinsertfront(plist, 4);
	printLNO(plist);

	LNOdelback(plist);
	printLNO(plist);

	LNOdelback(plist);
	printLNO(plist);

	LNOdelback(plist);
	printLNO(plist);

	LNOdelback(plist);
	printLNO(plist);

}

void test05()
{
	LNO* plist = LNOInit();
	LNOinsertfront(plist, 1);
	printLNO(plist);

	LNOinsertfront(plist, 2);
	printLNO(plist);

	LNOinsertfront(plist, 3);
	printLNO(plist);

	LNOinsertfront(plist, 4);
	printLNO(plist);

	LNOdelfront(plist);
	printLNO(plist);

	LNOdelfront(plist);
	printLNO(plist);

	LNOdelfront(plist);
	printLNO(plist);

	LNOdelfront(plist);
	printLNO(plist);

}

void test06()
{
	LNO* plist = LNOInit();
	LNOinsertfront(plist, 1);
	printLNO(plist);

	LNOinsertfront(plist, 2);
	printLNO(plist);

	LNOinsertfront(plist, 3);
	printLNO(plist);

	LNOinsertfront(plist, 4);
	printLNO(plist);

	LNO* find = Find(plist, 4);
	LNOinsertbefore( 5, find);
	printLNO(plist);

	find = Find(plist, 1);
	LNOinsertbefore(6, find);
	printLNO(plist);

	find = Find(plist, 3);
	LNOinsertbefore(7, find);
	printLNO(plist);
}


void test07()
{
	LNO* plist = LNOInit();
	LNOinsertfront(plist, 1);
	printLNO(plist);

	LNOinsertfront(plist, 2);
	printLNO(plist);

	LNOinsertfront(plist, 3);
	printLNO(plist);

	LNOinsertfront(plist, 4);
	printLNO(plist);

	LNO* find = Find(plist, 4);
	LNOinsertafter(5, find);
	printLNO(plist);


    find = Find(plist, 1);
	LNOinsertafter(6, find);
	printLNO(plist);


	 find = Find(plist, 3);
	LNOinsertafter(7, find);
	printLNO(plist);
}

void test08()
{
	LNO* plist = LNOInit();
	LNOinsertfront(plist, 1);
	printLNO(plist);

	LNOinsertfront(plist, 2);
	printLNO(plist);

	LNOinsertfront(plist, 3);
	printLNO(plist);

	LNOinsertfront(plist, 4);
	printLNO(plist);

	LNOinsertfront(plist, 5);
	printLNO(plist);

	LNO* find = Find(plist, 5);
	LNOdel(find);
	find = NULL;
	printLNO(plist);

    find = Find(plist, 3);
	LNOdel(find);
	find = NULL;
	printLNO(plist);

	find = Find(plist, 1);
	LNOdel(find);
	find = NULL;
	printLNO(plist);

}


void test09()
{
	LNO* plist = LNOInit();
	LNOinsertback(plist, 1);
	printLNO(plist);

	LNOinsertback(plist, 2);
	printLNO(plist);

	LNOinsertback(plist, 3);
	printLNO(plist);

	LNOinsertback(plist, 4);
	printLNO(plist);

	LNO* find = Find(plist, 4);
	LNOmodify(find, 5);
	printLNO(plist);

	find = Find(plist, 3);
	LNOmodify(find, 6);
	printLNO(plist);

	find = Find(plist, 2);
	LNOmodify(find, 7);
	printLNO(plist);

	find = Find(plist, 1);
	LNOmodify(find, 8);
	printLNO(plist);
}

void test10()
{
	LNO* plist = LNOInit();
	LNOinsertback(plist, 1);
	printLNO(plist);

	LNOinsertback(plist, 2);
	printLNO(plist);

	LNOinsertback(plist, 3);
	printLNO(plist);

	LNOinsertback(plist, 4);
	printLNO(plist);

	LNOdestory(plist);
	plist = NULL;
}

int main()
{
	test10();
	return 0;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1643545.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

基于JSP的酒店客房管理系统(二)

目录 第二章 相关技术介绍 2.1 Jsp的简介 2.2 sql server 2005 的简介 第三章 系统的分析与设计 3.1 系统需求分析 1&#xff0e;理解需求 2&#xff0e;需求分析 3.2开发及运行环境 3.3功能模块的设计 3.3.1 设计目标 3.3.2 客房管理系统前台的设计 3.3.3 操作员管…

牛角源码 | 【高级版】威客点赞悬赏任务源码/赚钱系统抖音快手火山悬赏任务站

点赞任务平台 无BUG完美自营版&#xff01; 霸屏天下赚钱系统源码/抖音快手火山悬赏任务平台 全新ui界面修复升级版任务系统&#xff0c;非市场垃圾版本。非常适合运营&#xff01; 下载地址&#xff1a;【高级版】威客点赞悬赏任务源码/赚钱系统抖音快手火山悬赏任务站 - 牛…

深入解析I2C协议:通讯简化之道

在现代电子系统中&#xff0c;组件间的通信是必不可少的。而I2C协议&#xff08;Inter-Integrated Circuit&#xff09;&#xff0c;由Philips Semiconductor&#xff08;现为NXP Semiconductors&#xff09;在1980s初期发明&#xff0c;已成为一种广泛使用的串行通信协议。其设…

cocos=》 预乘、混合(黑边、白色)

简介 预乘&#xff0c;指的是在数据提交给GPU之前&#xff0c;就对纹理的RGB分量与alpha值进行计算。 预乘计算 结果颜色 源颜色值 目标颜色值 * (1 - 源 alpha 值) result source.RGB dest.RGB * (1 - source.A); 对应的颜色混合函数设置为 gl.blendFunc(gl.ONE, gl.…

【动态规划】投资问题

本文利用markdown基于https://blog.csdn.net/qq_41926985/article/details/105627049重写,代码部分为本人编辑 代码要求 应用动态规划方法&#xff0c;求解投资问题&#xff0c;实现下面的例子。 #define MAX_N 4 //最大投资项目数目 #define MAX_M 5 //最大投资钱数(万元) /…

文献阅读:SPACEL:基于深度学习的空间转录组结构表征

文献介绍 「文献题目」 SPACEL: deep learning-based characterization of spatial transcriptome architectures 「研究团队」 瞿昆&#xff08;中国科学技术大学&#xff09; 「发表时间」 2023-11-22 「发表期刊」 Nature Communications 「影响因子」 16.6 「DOI」 10.…

ICode国际青少年编程竞赛- Python-1级训练场-基础训练2

ICode国际青少年编程竞赛- Python-1级训练场-基础训练2 1、 a 4 # 变量a存储的数字是4 Dev.step(a) # 因为变量a的值是4&#xff0c;所以Dev.step(a)就相当于Dev.step(4)2、 a 1 # 变量a的值为1 for i in range(4):Dev.step(a)Dev.turnLeft()a a 1 # 变量a的值变为…

[C++初阶]string类

1. 为什么要学习string类 1.1 C语言中的字符串 C语言中&#xff0c;字符串是以\0结尾的一些字符的集合&#xff0c;为了操作方便&#xff0c;C标准库中提供了一些str系列的库函数&#xff0c; 但是这些库函数与字符串是分离开的&#xff0c;不太符合OOP(面向对象)的思想&…

JAVA中的lambda表达式(无废话)

Lambda表达式是Java SE 8中一个重要的新特性。 它是一种语法形式&#xff0c;可以代码书写更加精炼。 用人话说就是把原来的代码变得很短。 这部分的内容是非常简单的。 一、函数式接口 想要理解lambda表达式&#xff0c;首先要了解函数式接口。 关于接口的知识请查阅&am…

浏览器中不能使用ES6的扩展语法...报错

浏览器大多数已经支持ES6&#xff08;ECMAScript 2015&#xff09;的扩展语法&#xff08;...&#xff09;&#xff0c;包括Chrome、Firefox、Safari和Edge等。然而&#xff0c;如果你在某些浏览器中遇到无法使用扩展语法的问题&#xff0c;可能是由以下原因导致的&#xff1a;…

ngrinder项目-本地调试遇到的坑

前提-maven mirrors配置 <mirrors><!--阿里公有仓库--><mirror><id>nexus-aliyun</id><mirrorOf>central</mirrorOf><name>Nexus aliyun</name><url>http://maven.aliyun.com/nexus/content/groups/public</ur…

每周打靶VulnHub靶机-DOUBLETROUBLE_ 1

doubletrouble: 1靶机传送门 get flags 靶机名为 双重麻烦&#xff0c;可能会繁琐一点 1.信息搜集 使用nmap进行域内存活主机扫描继续扫描其开放端口开放了22(ssh)、80(http)端口使用浏览器访问其80端口是一个登录页面&#xff0c;继续扫描其 敏感目录dirsearch -u [http://19…

通过helm在k8s上安装minio

1 helm安装minio 1.1 下载minio 添加仓库 helm repo add bitnami https://charts.bitnami.com/bitnami 将minio拉取下来 helm pull bitnami/minio --version 版本号 解压到本地开始编辑配置文件 tar -zxf minio-xxx.tgz [rootk8s-master01 minio]# vi values.yaml 1.2…

拼多多多多搜索推广技巧

拼多多多多搜索推广技巧主要包括以下几个方面&#xff1a; 拼多多推广可以使用3an推客。3an推客&#xff08;CPS模式&#xff09;给商家提供的营销工具&#xff0c;由商家自主设置佣金比例&#xff0c;激励推广者去帮助商家推广商品链接&#xff0c;按最终有效交易金额支付佣金…

Docker新建容器 修改运行容器端口

目录 一、修改容器的映射端口 二、解决方案 三、方案 一、修改容器的映射端口 项目需求修改容器的映射端口 二、解决方案 停止需要修改的容器 修改hostconfig.json文件 重启docker 服务 启动修改容器 三、方案 目前正在运行的容器 宿主机的3000 端口 映射 容器…

重要!!!方法的进阶使用------回调函数

参考资料&#xff1a; 参考视频 下面所有举的例子都在参考demo中 概述&#xff1a; 回调函数很简单&#xff0c;就是对普通方法参数的类型的拓展&#xff0c;其实是对普通方法的深层应用&#xff1b;回调函数其实就是将含有执行方法类的实例&#xff0c;以参数的形式传入到方…

集成学习算法:AdaBoost详解以及代码实现

本文尽量从一个机器学习小白或是只对机器学习算法有一个大体浅显的视角入手&#xff0c;尽量通俗易懂的介绍清楚AdaBoost算法&#xff01; 一、AdaBoost简介 AdaBoost&#xff0c;是英文"Adaptive Boosting"&#xff08;自适应增强&#xff09;的缩写&#xff0c;由…

【Linux】进程间通信 - 管道

文章目录 1. 进程间通信介绍1.1 进程间通信目的1.2 进程间通信发展1.3 进程间通信分类 2. 管道2.1 什么是管道2.2 匿名管道2.3 用 fork 来共享管道原理2.4 站在文件描述符角度 - 深入理解管道2.5 站在内核角度 - 管道本质2.6 管道读写规则2.7 管道特点 3. 命名管道3.1 匿名管道…

运行时数据区-基础

运行时数据区-基础 为什么学习运行时数据区Java内存区域&#xff08;运行时数据区域&#xff09;和内存模型&#xff08;JMM&#xff09; 区别组成部分&#xff08;jdk1.7 / jdk1.8&#xff09;从线程隔离性分类与类加载的关系每个区域的功能参考文章 为什么学习运行时数据区 …

【云原生】Docker 的网络通信

Docker 的网络通信 1.Docker 容器网络通信的基本原理1.1 查看 Docker 容器网络1.2 宿主机与 Docker 容器建立网络通信的过程 2.使用命令查看 Docker 的网络配置信息3.Docker 的 4 种网络通信模式3.1 bridge 模式3.2 host 模式3.3 container 模式3.4 none 模式 4.容器间的通信4.…