시퀀스 목록 및 연결 목록의 C 언어 구현 (단일, 단일 루프, 이중 루프)

이 블로그는 주로 시퀀스 테이블과 리드 노드 연결 목록 (단일 연결 목록, 단일 순환 연결 목록, 이중 원형 연결 목록)의 구현에 관한 것입니다.


코드에 대한 링크는 헤드 노드없이 링크 된 목록의 끝에 첨부됩니다.
 
 
 
 

1. 시퀀스 테이블 :

시퀀스 테이블은 연속적인 물리적 주소를 가진 메모리 셀을 사용하여 데이터 요소를 저장하는 선형 구조입니다. 일반적으로 사용되는 배열과 같은.

1. 시퀀스 테이블 정의

typedef struct Seqlist
{
	size_t size;//计数
	size_t capacity;//容量
	ElementType *base;//数据类型,ElementType=int 这里进行宏替换是为了方便存储不同的数据时,只需要更改宏即可
}Seqlist;

 
 
 
2. 시퀀스 테이블 초기화

void SeqlistInit(Seqlist *plist)
{
	assert(plist != NULL);
	plist->capacity = SEQNUMBER;//容量的大小,使用宏
	plist->size = 0;
	plist->base = (ElementType *)malloc(sizeof(ElementType)*SEQNUMBER);
}

 
 
 
3. 시퀀스 테이블 추가 : 첫 번째 삽입, 마지막 삽입, 위치 삽입, 값 삽입

void SeqlistPushFront(Seqlist *plist, ElementType x)//头插
{
	assert(plist != NULL);
	if (IsFull(plist))//这里调用了一个是否为满的函数,直接判断size和capacity是否相等即可
	{
		printf("顺序表已满,%d不能插入\n", x);
		return;
	}
	for (size_t i = plist->size; i > 0; i--)//方法画图讲解
	{
		plist->base[i] = plist->base[i - 1];
	}
	plist->base[0] = x;//头插,放到最前面
	plist->size++;//插入后计数要增加
}

void SeqlistPushBack(Seqlist *plist, ElementType x)//尾插
{
	assert(plist != NULL);
	if (IsFull(plist) && !SeqlistInc(plist))
	{
		printf("顺序表已满,%d不能插入\n", x);
		return;
	}
	plist->base[plist->size] = x;//直接插入即可
	plist->size++;//插入后计数要增加
}


void SeqlistInsertPos(Seqlist *plist, int pos, ElementType x)//位置插入
{
	assert(plist != NULL);
	if (IsFull(plist))//满了不能插入
	{
		printf("顺序表已满,%d不能插入\n", x);
		return;
	}
	if ( pos<0 || pos>(int)plist->size)//位置有误,顺序表前面都为空,不能前面为空(第一个除外)的地方插,要连续着
	{
		printf("输入的位置%d非法,%d不能插入\n",pos,x);
		return;
	}
	for (size_t i = plist->size; i > (size_t) pos; i--)//位置正确,插入
	{
		plist->base[i] = plist->base[i - 1];
	}
	plist->base[pos] = x;
	plist->size++;
}


void SeqlistInsertVal(Seqlist *plist, ElementType x)//值插入,插入前先排序
{
	assert(plist != NULL);
	qsort(plist->base, plist->size, sizeof(ElementType), cmp);//这里使用了库函数 ,qsort
	if (IsFull(plist) && !SeqlistInc(plist))
	{
		printf("顺序表已满,%d不能插入\n", x);
		return;
	}
	size_t end = plist->size - 1;
	while (end >= 0 && x < plist->base[end])
	{
		plist->base[end + 1] = plist->base[end];
		end--;
	}
	plist->base[end + 1] = x;
	plist->size++;
}

여기에 사진 설명 삽입
시퀀스 테이블이이 부분에 삽입됩니다. 기본 아이디어는 데이터가 뒤로 이동하고 마지막으로 삽입 된 위치에서 값을 덮어 쓰는 것입니다.
 
 
 
4. 시퀀스 테이블 삭제 : 첫 번째 삭제, 마지막 삭제, 위치 삭제

void SeqlistPopFront(Seqlist *plist)//头删
{
	assert(plist != NULL);
	if (IsEmpty(plist))
	{
		printf("数据为空,无法删除\n");
		return;
	}
	for (size_t i = 0; i < plist->size; i++)
	{
		plist->base[i] = plist->base[i + 1];//后面的数据往前覆盖
	}
	plist->size--;//元素个数减小
}

void SeqlistPopBack(Seqlist *plist)//尾删
{
	assert(plist != NULL);
	if (IsEmpty(plist))
	{
		printf("数据为空,无法删除\n");
		return;
	}
	plist->size--;//直接个数减小,将最后一个位置设置为无效
}

void SeqlistEraseBack(Seqlist *plist, int pos)//位置删除
{
	assert(plist != NULL);
	if (IsEmpty(plist))
	{
		printf("顺序表为空,不能删除!\n");
		return;
	}
	if (pos < 0 || pos > (int)plist->size)
	{
		printf("输入的位置%d非法,不能删除\n",pos);
		return;
	}
	for (size_t i = (size_t)pos; i < plist->size ; i++)
	{
		plist->base[i] = plist->base[i + 1];//后面数据往前覆盖
	}
	plist->size--;//元素个数减小
}

시퀀스 테이블 삭제 : 기본 아이디어는 다음 데이터를 하나씩 덮어 쓰고 마지막으로 숫자를 줄이는 것입니다.
 
 
 
5. 시퀀스 테이블 확인 : 위치 검색, 이진 검색

int SeqlistFind(Seqlist *plist, ElementType x)//位置查找
{
	assert(plist != NULL);
	if (IsEmpty(plist))
	{
		printf("数据为空,无法查找!\n");
	}
	size_t pos = 0;
	while (pos < plist->size && x != plist->base[pos])//位置要合法,数据不等于就继续执行
	{
		pos++;
	}
	if (pos == plist->size)//跳出后,若是因为位置不合法,则一定是数据不存在
	{
		return -1;
	}
	return pos;//数据找到了,返回位置
}

void SeqlistBinaryFind(Seqlist *plist, ElementType x)
{
	assert(plist != NULL);
	size_t start = 0;
	size_t end = plist->size;
	size_t mid = 0;
	while (start <= end)//二分查找
	{
		mid = (start + end) / 2;
		if (x < plist->base[mid])//比中间小,往左走,end变为mid-1
		{
			end = mid - 1;
		}
		else if (x > plist->base[mid])//比中间大,往右走,start变为mid+1
		{
			start = mid + 1;
		}
		else//找到了
		{
			printf("数据查找成功,为:>%d\n", plist->base[mid]);
			return;
		}
	}
	printf("查找失败,数据%d不存在\n", x);//跳出循环,就是数据不存在
}

 
 
 
5. 시퀀스 테이블 정렬 : 시퀀스 테이블은 연속적인 공간이며 직접 값 교환으로 정렬 할 수 있습니다.

void SeqlistSort(Seqlist *plist)//数据排序,这里使用qsort
{
	assert(plist != NULL);
	if (IsEmpty(plist))
	{
		printf("数据为空,无法排序!\n");
	}
	qsort(plist->base, plist->size, sizeof(ElementType), cmp);//直接使用qsort库函数
}
//qsort需要自己写一个比较函数,作为参数传为qsort
int cmp(const void *a, const void *b)
{
	return *(ElementType *)a - *(ElementType *)b;
}

여기에서 버블 링 방법을 사용하여 교환을 비교할 수도 있습니다.
 
 
 
6. 시퀀스 테이블의 반전 : 시퀀스 테이블이 반전됩니다.

void SeqlistReverse(Seqlist *plist)//数据倒序
{
	assert(plist != NULL);
	size_t start = 0;
	size_t end = plist->size - 1;
	while (start < end)//每一位进行值交换
	{
		plist->base[end] ^= plist->base[start];
		plist->base[start] ^= plist->base[end];
		plist->base[end] ^= plist->base[start];
		start++, end--;
	}
}

데이터의 XOR은 비트를 교환하고 데이터 교환의 효과를 얻을 수 있습니다.
예 :
여기에 사진 설명 삽입
 
 
 
7. 시퀀스 테이블에서 중복 요소 삭제

//比如:1 2 2 2 2 3 3 3 4
void SeqlistEraseAll(Seqlist *plist,ElementType x)//删除指定元素
{
	assert(plist != NULL);
	if (plist->size <= 0)
	{
		return;
	}
	SeqlistSort(plist);//排序
	size_t pos = SeqlistFind(plist, x);//找到第一个出现的位置
	size_t count = 0;//计数有多少个
	int tmp = 0;
	for (size_t i = pos; i < plist->size; i++)
	{
		tmp = 1;//标志位
		if (x == plist->base[i])
		{
			tmp = 0;
			count++;
		}
		if (tmp == 1)
		{
			break;//走到这,说明已经没有相等了,进行跳出
		}
	}
	tmp = count;//保留个数
	int number = count + pos;//记住要开始替换的位置
	for (; count > 0; count--)
	{
		plist->base[pos++] = plist->base[number++];//后面的数据往前面覆盖
	}
	plist->base[pos-1] = plist->base[number-1];//循环做完,会少替换一次
	plist->size -= tmp;
}

시퀀스 테이블의 장점 :

  1. 구현하기 쉬움
  2. 랜덤 액세스

용량보다 더 많은 공간이 필요하면 어떻게합니까?
다음과 같이 동적 개발을 사용하여 공간을 다시 열 수 있습니다.

bool SeqlistInc(Seqlist *plist)//扩容
{
	assert(plist != NULL);
	ElementType *s = (ElementType *)realloc(plist->base, 2 * plist->capacity * sizeof(ElementType));
	if (NULL != s)
	{
		plist->base = s;
		plist->capacity = 2 * plist->capacity;
		return true;
	}
	return false;
}

하지만 단점도있다. 동적으로 개발하는 동안 공간이 두 배로 늘어나고, 원래 공간 하나의 용량 만 초과하면 많은 공간 낭비가 발생한다.

 
 
 
 

2. 단일 연결 목록 :

연결 목록은 물리적 저장 구조에서 비 연속적이고 비 순차적 인 저장 구조로 데이터 요소의 논리적 순서는 연결 목록에있는 포인터의 연결 순서에 의해 실현됩니다.

아래에 구현 된 리드 노드의 단일 연결 목록
 
 
 
1. 단일 연결 목록의 정의 :

typedef struct SlistNode
{
	ElementType data;//数据
	struct SlistNode *next;//指向下一个的指针
}SlistNode;

 
 
 
2. 단일 연결 목록 초기화 :

void SlistIniHead(SList *phead)
{
	assert(phead != NULL);
	(*phead) = (SlistNode*)malloc(sizeof(SlistNode));//带有头节点,头节点不放数据,不能更改
	(*phead)->next = NULL;
}

 
 
 
3. 단일 연결 목록 증가 : 머리 삽입, 꼬리 삽입, 값 삽입

void SlistHeadPushFront(SList *phead, ElementType x)//有头节点,头插
{
	assert(phead != NULL);
	SlistNode *p = (SlistNode *)malloc(sizeof(SlistNode));//申请空间
	assert(p != NULL);
	p->data = x;
	SlistNode *q = *phead;
	p->next = q->next;//p的下一个为头的下一个节点
	q->next = p;//头的下一个为p
}

void SlistHeadPushBack(SList *phead, ElementType x)//有头节点,尾插
{
	assert(phead != NULL);
	SlistNode *q = *phead;
	while (q->next != NULL)//找到最后一个节点
	{
		q = q->next;
	}
	SlistNode *p = (SlistNode *)malloc(sizeof(SlistNode));
	p->data = x;
	p->next = NULL;//尾部为空
	q->next = p;//前面节点指向p
}

void SlistHeadInsertVal(SList *phead, ElementType x)//值插入
{
	assert(phead != NULL);
	SlistHeadSort(phead);//先排序
	SlistNode *p = (SlistNode *)malloc(sizeof(SlistNode));
	assert(p != NULL);
	p->data = x;
	SlistNode *q = (*phead)->next,*prev = NULL;
	while (q != NULL && q->data < x)//寻找插入的位置
	{
		prev = q;//前驱节点
		q = q->next;
	}
	if (prev == NULL)
	{
		p->next = (*phead)->next;
		(*phead)->next = p;
	}
	else
	{
		p->next = prev->next;
		prev->next = p;
	}
}

값 삽입 블록에서는 링크드리스트가 랜덤 액세스를 지원하지 않기 때문에 위치 삽입을 할 수 없습니다.
 
 
 
4. 단일 연결리스트 삭제 : 헤드 삭제, 테일 삭제, 값 삭제

void SlistHeadPopFront(SList *phead)
{
	assert(phead != NULL);
	if ((*phead)->next == NULL)//链表为空,不能删除
	{
		return;
	}
	SlistNode *p = (*phead)->next;//删除头节点的下一个并且指向第一个节点的next
	(*phead)->next = p->next;
	free(p);//释放,因为链表的节点都是malloc开辟的,所有需要释放
}

void SlistHeadPopBack(SList *phead)//尾删
{
	assert(phead != NULL);
	if ((*phead)->next == NULL)//链表为空,不能删除
	{
		return;
	}
	SlistNode *p = (*phead)->next;
	SlistNode *prev = NULL;//前驱
	while (p->next != NULL)
	{
		prev = p;
		p = p->next;
	}
	prev->next = NULL;//前驱的下一个置空
	free(p);//释放尾节点
}

void SlistHeadEraseVal(SList *phead, ElementType x)//值删
{
	assert(phead != NULL);
	SlistNode *p = SlistHeadFind(phead, x);//找到删除值的位置
	if (p == NULL)
	{
		printf("数据不存在,无法删除!\n");
		return;
	}
	SlistNode *q = (*phead)->next,*prev;
	if (q == p)//第一个节点就为释放的节点,改变头节点的指向
	{
		(*phead)->next = q->next;
		free(q);
		return;
	}
	else
	{
		while (q != NULL)
		{
			prev = q;//前驱
			q = q->next;
			if (q == p)//相等了
			{
				prev->next = q->next;
				free(q);
				return;
			}
		}
	}
}

헤더 삭제, 꼬리 삭제, 값 삭제 등 단일 연결 목록에서이 부분을 삭제합니다. 헤드 노드는 변경할 수 없으며 삭제할 노드가 헤드 노드의 다음 노드인지 여부에주의해야합니다. 테일 노드가 삭제되면 선행 노드를 찾고 선행 노드의 후속 노드는 비워 두어야합니다. 값이 삭제되면 삭제할 값의 위치를 ​​직접 찾을 수 있습니다.
 
 
 
5. 단일 링크 목록 검색 : 값 검색, 반환 위치 찾기

SlistNode *SlistHeadFind(SList *phead, ElementType x)
{
	assert(phead != NULL);
	SlistNode *p = (*phead)->next;
	while (p != NULL)//循环查找,这里不能做二分查找,因为链表不支持随机访问
	{
		if (p->data == x)
		{
			return p;
		}
		p = p->next;
	}
	return NULL;
}

 
 
 
6. 단일 연결 목록 정렬 : 단일 연결 목록의 정렬은 시퀀스 테이블과 다릅니다. 시퀀스 테이블은 값을 직접 교환 할 수 있습니다. 연결 목록에서는 일반적으로 정렬 목적을 달성하기 위해 노드 간의 방향이 변경됩니다.

void SlistHeadSort(SList *phead)
{
	assert(phead != NULL);
	SlistNode *tmp = (*phead)->next,*p = (*phead)->next, *prev = NULL;//p为头节点下一节点
	SlistNode *q = p->next;//q为p的下一个节点
	p->next = NULL;//将p的next指向断开
	while (q != NULL)
	{
		p = q;
		q = q->next;
		while (tmp != NULL && p->data > tmp->data)
		{
			prev = tmp;
			tmp = tmp->next;
		}
		if (prev == NULL)//如果前驱为空,说明p的值比tmp的值要下,头节点的下一节点要更换
		{
			p->next = (*phead)->next;
			(*phead)->next = p;
		}
		else
		{
			p->next = prev->next;
			prev->next = p;
		}
		tmp = (*phead)->next;//tmp继续指向头的下一指针
		prev = NULL;
	}
}

처음에는 이런 종류의 노드가 있습니다 :
여기에 사진 설명 삽입
p의 꼬리 포인터를 분리합니다 :
여기에 사진 설명 삽입
tmp 노드의 값과 p의 값을 하나씩 판단합니다. 또한 p 노드의 데이터가 tmp 데이터 인 p 노드보다 작을 때를 결정하는 전구체 포인터가 있습니다. 삽입 위치입니다.
 
 
 
7. 단일 연결 목록의 반전 : 정렬과 유사하게 직접 연결이 끊어 지지만 비교없이 직접 교환 할 수 있습니다.

void SlistHeadReverse(SList *phead)//逆置
{
	assert(phead != NULL);
	SlistNode *p = (*phead)->next;
	SlistNode *q = p->next;
	p->next = NULL;
	while (q != NULL)
	{
		p = q;
		q = q->next;
		p->next = (*phead)->next;
		(*phead)->next = p;
	}
}

 
 
 
8. 단일 연결 목록에서 중복 값 삭제

void SlistHeadEraseAll(SList *phead, ElementType x)
{
	assert(phead != NULL);
	SlistHeadSort(phead);//先进行排序
	SlistNode *p = (*phead)->next, *tmp = NULL, *prev = *phead;
	while (p != SlistHeadFind(phead, x))//找到第一个出现x的节点,prev等于出现这个节点的前驱
	{
		prev = p;
		p = p->next;
	}
	while (p != NULL && p->data == x)//进行逐个释放
	{
		tmp = p;
		p = p->next;
		free(tmp);
	}
	prev->next = p;//前驱的下一个等于不为x的节点
}

단일 연결 목록에서는 헤드 노드의 변환을 고려하지 않기 때문에 구현이 더 간단합니다. 헤드 노드가없는 경우 정렬 반전에서 첫 번째 노드의 변경을 고려해야합니다. 그리고 연결 목록은 무작위로 접근 할 수 없습니다.

 
 
 
 

세 개의 단일 순환 연결 목록 :

꼬리 노드의 점이 비어 있지 않지만 헤드 노드를 다시 가리킨다는 점을 제외하면 단일 연결 목록과 유사합니다.

 
 
 
1. 단일 순환 연결 목록 생성 :

typedef struct SCListNode
{
	ElementType data;
	struct SCListNode *next;
}SCListNode;

typedef SCListNode* SCList;

 
 
 
2. 단일 순환 연결 목록 초기화 :

void SCListHeadIni(SCList *phead)//链表初始化
{
	*phead = _BuyNode(0);//这里这个buynode是进行开辟节点时定义的一个函数
	(*phead)->next = *phead;
}

SCListNode *_BuyNode(ElementType v)
{
	SCListNode *_s = (SCListNode *)malloc(sizeof(SCListNode));
	_s->data = v;
	_s->next = _s;
	return _s;
}

 
 
 
3. 단일 원형 연결 목록 증가 : 머리 삽입, 꼬리 삽입, 값 삽입

void SCListHeadPushFront(SCList *phead, ElementType x)//头插法
{
	assert(phead != NULL);
	SCListNode *p = _BuyNode(x);
	assert(p != NULL);
	SCListNode *head = *phead;
	if (head->next != *phead)//判断头节点是否为单循环
	{
		p->next = (*phead)->next;
		(*phead)->next = p;
	}
	else
	{
		p->next = *phead;
		(*phead)->next = p;
	}
}

void SCListHeadPushBack(SCList *phead, ElementType x)//尾插法
{
	assert(phead != NULL);
	SCListNode *p = _BuyNode(x);
	assert(p != NULL);
	SCListNode *head = (*phead)->next;
	while (head->next != *phead)//找到最后一个节点
	{
		head = head->next;
	}
	head->next = p;
	p->next = *phead;//节点下一个指向头
}

void SCListHeadInsertVal(SCList *phead, ElementType x)//插入,插入前先排序
{
	assert(phead != NULL);
	SCListHeadSort(phead);
	SCListNode *s = _BuyNode(x);
	SCListNode *p = (*phead)->next, *prev = NULL;
	while (p != *phead && p->data < x)//找到插入位置
	{
		prev = p;
		p = p->next;
	}
	if (prev == NULL)//第一个位置就可插入
	{
		s->next = (*phead)->next;
		(*phead)->next = s;
	}
	else//找到了插入位置,也有前驱,可以插入了
	{
		s->next = prev->next;
		prev->next = s;
	}
}

 
 
 
4. 단일 순환 연결 목록 삭제 : 헤드 삭제, 꼬리 삭제, 값 삭제

void SCListHeadPopFront(SCList *phead)//头删
{
	assert(phead != NULL);
	SCListNode *p = (*phead)->next;
	if (p == *phead)//就只有一个头节点,不能删除
	{
		return;
	}
	(*phead)->next = p->next;//改变指向
	free(p);
}

void SCListHeadPopBack(SCList *phead)//尾删
{
	assert(phead != NULL);
	SCListNode *p = (*phead)->next, *prev = NULL;
	if (p == *phead)//只有头节点
	{
		return;
	}
	while (p->next != *phead)//找到尾节点
	{
		prev = p;
		p = p->next;
	}
	if (prev == NULL)//只有两个节点,更换头节点指向
	{
		(*phead)->next = *phead;
		free(p);
	}
	else 
	{
		prev->next = *phead;//改变指向,释放
		free(p);
	}
}

void SCListHeadEraseVal(SCList *phead, ElementType x)//删除
{
	assert(phead != NULL);
	SCListNode *p = (*phead)->next, *prev=NULL;
	if (p == *phead)
	{
		return;
	}
	while (p != *phead && p->data != x)//找到要删除节点的位置
	{
		prev = p;
		p = p->next;
	}
	if (prev == NULL)//头节点的下一个删除
	{
		(*phead)->next = p->next;
		free(p);
	}
	else//找到中间的删除位置
	{
		prev->next = p->next;
		free(p);
	}
}

단일 순환 연결 목록의이 부분을 삭제하려면 꼬리 노드에주의를 기울이고 끝에서 삭제할 때는 선행 노드를 헤드 노드에 가리 킵니다.

 
 
 
5. 단일 순환 연결 목록 확인 : 노드 위치 반환

SCListNode* SCListHeadFind(SCList *phead, ElementType x)//查找数据
{
	assert(phead != NULL);
	SCListNode *p = (*phead)->next;
	while (p != *phead)
	{
		if (p->data == x)
		{
			return p;
		}
		p = p->next;
	}
	return NULL;
}

 
 
 
6. 단일 순환 연결 목록 정렬 : 노드 간 방향을 사용하여 순서 변경

void SCListHeadSort(SCList *phead)//链表排序
{
	assert(phead != NULL);
	if (SCListLength(phead) <= 1)
	{
		return;
	}
	SCListNode *p = (*phead)->next,*tmp= (*phead)->next,*prev=NULL;
	SCListNode *q = p->next;
	p->next = *phead;//断开,指向头
	while (q != *phead)
	{
		p = q;
		q = q->next;
		while (tmp != *phead && p->data > tmp->data)//寻找插入位置
		{
			prev = tmp;
			tmp = tmp->next;
		}
		if (prev == NULL)//第一个位置插入
		{
			p->next = (*phead)->next;
			(*phead)->next = p;
		}
		else//中间插入
		{
			p->next = prev->next;
			prev->next = p;
		}
		tmp = (*phead)->next;//tmp继续指向头的下一个
		prev = NULL;
	}
}

 
 
 
7. 단일 순환 연결 목록의 반전 :

void SCListHeadReverse(SCList *phead)//链表逆置
{
	assert(phead != NULL);
	if (SCListLength(phead) <= 1)
	{
		return;
	}
	SCListNode *p = (*phead)->next;
	SCListNode *q = p->next;
	p->next = *phead;//断开
	while (q != *phead)
	{
		p = q;
		q = q->next;
		p->next = (*phead)->next;//更改指向
		(*phead)->next = p;
	}
}

 
 
 
8. 단일 순환 연결 목록에서 중복 값 삭제 : 먼저 정렬

void SCListHeadEraseAll(SCList *phead, ElementType x)//清除重复元素
{
	assert(phead != NULL);
	SCListHeadSort(phead);//排序
	SCListNode *p = (*phead)->next, *prev = NULL,*q=NULL;
	while (p != SCListHeadFind(phead, x))//找到第一个清除位置
	{
		prev = p;
		p = p->next;
	}
	while (p != *phead && p->data == x)//清除
	{
		q = p;
		p = p->next;
		free(q);
	}
	if (prev == NULL)//第一个就可以清除
	{
		(*phead)->next = p;
	}
	else//中间位置
	{
		prev->next = p;
	}
}

단일 순환 연결 목록에서 마지막 노드는 루프백되어야합니다. 그렇지 않으면 오류가 발생합니다.

 
 
 
 

네, 이중 순환 연결 목록 :

이중 순환 연결 목록은 각 노드에 두 개의 포인터, 하나의 선행 작업과 하나의 후속 작업이 있다는 것입니다.

헤드 노드가있는 이중 순환 연결 목록 :
여기에 사진 설명 삽입

 
 
 
1. 이중 순환 연결 목록의 정의 :

typedef struct DCListNode
{
	ElementType data;
	struct DCListNode *prev;//前驱
	struct DCListNode *next;//后继
}DCListNode;

typedef DCListNode* DCList;

 
 
 
2. 이중 순환 연결 목록 초기화

void DCListHeadIni(DCList *phead)//链表初始化
{
	*phead = _BuyNode(0);//头节点
}

DCListNode *_BuyNode(ElementType v)
{
	DCListNode *_s = (DCListNode *)malloc(sizeof(DCListNode));
	_s->data = v;
	_s->next = _s->prev = _s;
	return _s;
}

 
 
 
3. 이중 원형 연결 목록 증가 : 머리 삽입, 꼬리 삽입, 값 삽입

void DCListHeadPushFront(DCList *phead, ElementType x)//头插法
{
	assert(phead != NULL);
	DCListNode *p = *phead;
	DCListNode *s = _BuyNode(x);
	assert(s != NULL);
	s->next = p->next;//插入的节点后继等于头节点的后继
	s->prev = p;//前驱等于头节点
	s->next->prev = s;//下一个节点的前驱等于s
	s->prev->next = s;//前驱的后继等于s
}

void DCListHeadPushBack(DCList *phead, ElementType x)//尾插法
{
	assert(phead != NULL);
	DCListNode *p = *phead;
	DCListNode *s = _BuyNode(x);
	assert(s != NULL);
	s->prev = p->prev;//插入节点的前驱等于头节点的前驱
	s->next = p;//插入节点的后继等于头节点
	s->prev->next = s;//s前驱的后继等于s
	s->next->prev = s;s的后继的前驱等于s
}

void DCListHeadInsertVal(DCList *phead, ElementType x)//插入,插入前先排序
{
	assert(phead != NULL);
	DCListHeadSort(phead);//排序
	DCListNode *s = _BuyNode(x);
	DCListNode *p = (*phead)->next;
	DCListNode *q = p->next;
	while (p->data < x && p != *phead)//寻找插入位置
	{
		p = p->next;
	}
	s->next = p;//插入节点的后继等于p
	s->prev = p->prev;//s的前驱等于p的前驱
	s->prev->next = s;//s的前驱的后继等于s
	s->next->prev = s;//s的后继的前驱等于s
}


이중 순환 연결 목록의 전임자와 후계자의 두 포인터로 인해 구현하기가 특히 편리합니다. 노드의 전임자와 후계자를 찾기 위해서는 노드 만 알면되며 삽입, 삭제 및 정렬에서 잘 구현됩니다.

 
 
 
4. 이중 순환 연결 목록 삭제 : 헤드 삭제, 꼬리 삭제, 값 삭제

void DCListHeadPopFront(DCList *phead)//头删
{
	assert(phead != NULL);
	DCListNode *p = (*phead)->next;
	if (p != *phead)
	{
		p->prev->next = p->next;//直接改变指向
		p->next->prev = p->prev;
		free(p);
	}
}

void DCListHeadPopBack(DCList *phead)//尾删
{
	assert(phead != NULL);
	DCListNode *p = (*phead)->prev;
	if (p != *phead)
	{
		p->prev->next = p->next;//改变指向
		p->next->prev = p->prev;
		free(p);
	}
}

void DCListHeadEraseVal(DCList *phead, ElementType x)//删除
{
	assert(phead != NULL);
	DCListNode *p = DCListHeadFind(phead, x);//查找该节点的位置
	if (p == NULL)
	{
		return;
	}
	p->prev->next = p->next;//删除
	p->next->prev = p->prev;
	free(p);
}

이중 순환 연결 목록에서 노드를 삭제하려면 노드를 찾은 다음 선행 작업의 후속 작업이 후속 작업을 가리키고 후속 작업의 선행 작업이 선행 작업을 가리 키도록하면됩니다.

 
 
 
5. 이중 순환 연결 목록 확인 : 값 확인, 반환 위치

DCListNode* DCListHeadFind(DCList *phead, ElementType x)//查找数据
{
	assert(phead != NULL);
	DCListNode *p = (*phead)->next;
	while (p->data != x && p != *phead)//查找位置
	{
		p = p->next;
	}
	if (p == *phead)//为头节点,返回null
	{
		return NULL;
	}
	return p;
}

 
 
 
6. 이중 순환 연결 목록 정렬 :

void DCListHeadSort(DCList *phead)//链表排序
{
	assert(phead != NULL);
	if (DCListHeadLength(phead) <= 1)
	{
		return;
	}
	DCListNode *p = (*phead)->next;
	DCListNode *q = p->next;
	p->next = p->prev;//断开
	p->prev->prev = p;//断开,两个节点自循环
	while (q != *phead)
	{
		p = q;
		q = q->next;
		DCListNode *tmp = (*phead)->next;
		while (p->data > tmp->data && tmp != *phead)//查找插入位置
		{
			tmp = tmp->next;
		}
		p->prev = tmp->prev;//进行插入
		p->next = tmp;
		p->prev->next = p;
		p->next->prev = p;
	}

}

이 블록의 순서는 값 삽입과 동일하고 처음 두 개는 연결이 끊긴 다음 후속 시퀀스 값의 크기를 비교 한 다음 삽입을 수행합니다.

 
 
 
7. 이중 순환 연결 목록의 반전 :

void DCListHeadReverse(DCList *phead)//链表逆置
{
	assert(phead != NULL);
	if (DCListHeadLength(phead) <= 1)
	{
		return;
	}
	DCListNode *p = (*phead)->next;
	DCListNode *q = p->next;
	p->next = p->prev;
	p->prev->prev = p;//断开,两个节点自循环
	while (q != *phead)
	{
		p = q;
		q = q->next;
		p->prev = *phead;//插入
		p->next = (*phead)->next;
		p->prev->next = p;
		p->next->prev = p;
	}
}

이 블록의 반전은 헤드 삽입 방법과 유사하며 노드는 헤드 노드 바로 뒤에 삽입됩니다.

 
 
 
8. 이중 루프의 반복 된 값을 삭제합니다.

void DCListHeadEraseAll(DCList *phead, ElementType x)//清除和输入值在链表中一样的元素
{
	assert(phead != NULL);
	DCListHeadSort(phead);//排序
	DCListNode *p = (*phead)->next;
	if (p == *phead)//只有一个头节点
	{
		return;
	}
	while (p->data != x && p != *phead)//寻找删除位置
	{
		p = p->next;
	}
	while (p->data == x)//节点值相等,就一直删除
	{
		DCListNode *tmp = NULL;;
		p->prev->next = p->next;//改变前驱的指向
		p->next->prev = p->prev;//改变后的指向
		tmp = p;
		p = p->next;
		free(tmp);//释放
	}
}

이중 순환 연결 목록의 구현은 매우 편리하지만 구조가 가장 복잡합니다.

필요한 경우 여기에서 코드를 다운로드 할 수 있습니다 (연결된 목록에는 헤드리스 노드가 있습니다) : 코드
 
 
  
 
  
 
  
 
 
 
 
 
요약 : 시퀀스 테이블에서 구현하는 것이 매우 편리하지만 용량이 제한되어 있습니다. 더 많은 공간이 열리면 공간 낭비가 발생합니다. 연결 목록에는 헤드리스 노드 연결 목록, 단일, 단일 루프, 이중 및 이중 루프 등 많은 구조가 있습니다. 일반적으로 사용되는 연결 목록은 헤드리스 노드의 단일 연결 목록과 선행 노드의 이중 루프 연결 목록입니다.

추천

출처blog.csdn.net/w903414/article/details/107668597