[C언어 15] 단일 연결 리스트, (보조 포인터와 주 포인터 적용에 대한 자세한 설명)

단일 목록

1. 단일 연결 리스트 소개

Linked list는 물리적 저장 구조의 비순차적이고 비순차적인 저장 구조로, 데이터 요소의 논리적 순서는 연결 리스트에서 포인터의 연결 순서를 통해
구현 된다.

이전 블로그에서 시퀀스 테이블의 구조를 명확하게 볼 수 있었지만 연결 목록이 아닌 연결 목록의 링크는 포인터에 의해 안내되며,

하나의 데이터는 N 개의 메모리 공간으로 분리될 수 있지만 실제로는 연결되어 있습니다 연결 목록을 자세히 설명하기 위해 몇 가지 그림을 준비했습니다.
여기에 이미지 설명 삽입

단일 연결 목록은 서로 다른 노드의 위치를 ​​결정하기 위한 포인터가 있는 기차와 같아서 직렬로 연결할 수 있지만 한 가지 이해해야 합니다. 그림의 화살표는 가상이고 가상, 우리는 결정할 수 있습니다. 다음 노드 위치에는 포인터만 있습니다.

2. 단일 연결 리스트 구현

#pragma once
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
#include<malloc.h>
typedef int SeDataType;
//定义结点
typedef struct SeqList
{
    
    
	SeDataType data;
	struct SeqList* next;
}SeNode;
//开辟节点
SeNode* BuySeqListNode(SeNode* phead, SeDataType x)
//销毁
void SeqListDestroy(SeNode** phead);
//打印
void SeqListPrint(SeNode* phead);
//尾插
void SeqListPushBack(SeNode** phead, SeDataType x);
//尾删
void SeqLIstPopBack(SeNode** phead);
//头插
void SeqListPushFront(SeNode** phead, SeDataType x);
//头删
void SeqListPopFront(SeNode** phead);
//pos位置之后插入
void SeqListInsterAfter(SeNode* pos, SeDataType x);
//删除pos位置
void SeqListEraseAfter(SeNode* pos);

단일 연결 목록을 구현하려면 위의 인터페이스를 구현해야 합니다.

2.1.1 단일 연결 리스트 노드의 생성 및 소멸

//初始化
SeNode* BuySeqListNode(SeDataType x)
{
	SeNode* newnode = (SeNode*)malloc(sizeof(SeNode));//创建结点
	if (newnode == NULL)//判断结点是否生成成功
	{
		perror("BuySeqListNode");
		exit(-1);
	}
	newnode->data = x;//赋值
	newnode->next = NULL;
	return newnode;//返回结点地址
}
//销毁
void SeqListDestroy(SeNode** phead)
{
	assert(phead);//判断是否为空
	while (*phead)//循环free空间
	{
		SeNode* next = (*phead)->next;//记录下一个节点,避免链接丢失
		free(*phead);//释放
		*phead = next;
	}
	*phead = NULL;//置空
}

이 코드 문자열을 읽은 후, 우리는 이 두 함수가 완전히 다른 포인터 유형을 사용한다는 것을 알게 될 것입니다.첫 번째 수준 포인터는 노드를 여는 데 사용되고 두 번째 수준 포인터는 노드를 파괴하는 데 사용됩니다. 포인터에 대한 이해를 테스트하기 위해 블로그에서 가장 어려운 지식 포인트

여기에 이미지 설명 삽입

PushBack 함수를 호출한 후 현재 격리된 노드 노드를 성공적으로 생성했습니다.
여기에 이미지 설명 삽입

그래서 plist와 node를 연결하는 방법이 바로 포인터입니다. 이때 pilst를 사용하여 node를 연결하려면 plist의 주소를 전달해야 합니다. 주소를 전달해야만 plist의 지점을 변경할 수 있습니다. 만약 주소를 전달하지 마십시오. 후속 작업은 임시 사본일 뿐입니다.

이때 엉킨 점이 나타났습니다 제 plist는 포인터인데 왜 포인터의 주소를 넘겨야 할까요?

그림을 보자

이 그림은 포인터의 본질적인 차이점을 생생하게 보여줍니다. 포인터도 변수이고 변수에도 주소가 있습니다. 변수를 변경하려면 주소를 통해 변경해야 합니다. plist를 수정할 때 plist는 그러나 저장하는 값을 변경하려면 자체 주소를 전달해야 하므로 보조 포인터를 전달해야 합니다.

2.1.2 단일 연결 목록 테일 플러그

꼬리 삽입 작업을 수행하는 방법은 우리가 만든 Sqnode 유형에서 next가 다음 노드를 가리키는 것을 이해해야 합니다.따라서 꼬리 삽입을 원하면 next to link의 이전 노드를 찾아야 합니다.

void SeqListPushBack(SeNode** phead, SeDataType x)
{
	assert(phead);//判断指针是否为空
	SeNode* node = BuySeqListNode(x);//开辟节点
	if (*phead == NULL)//如果*phead为空,代表链表为空,之间赋值替换即可
	{
		*phead = node;
	}
	else
	{
		SeNode* tail = *phead;
		while (tail->next != NULL)//如果不为空,则遍历链表,在最后一个结点进行链接
		{
			tail = tail->next;
		}
		tail->next = node;
	}
}

이 시점에서 우리는 테일 플러그 인터페이스 구현을 완료했습니다.프로그램이 올바른지 판단하는 방법은 무엇입니까?디버깅을 통해 직관적으로 관찰하거나 미리 인쇄 인터페이스를 관찰할 수 있습니다.우리는 인쇄 인터페이스 구현을 사용합니다.

2.1.3 단일 연결 목록 인쇄

포인터를 변경하는 작업 없이 인쇄하기를 원하기 때문에 전체 배열을 탐색하기 위해 첫 번째 수준 포인터만 전달하면 됩니다.

void SeqListPrint(SeNode* phead)
{
	SeNode* cur = phead;//记录位置
	while (cur)
	{
		printf("%d ", cur->data);//打印
		cur = cur->next;//赋值遍历
	}
}

여기에 이미지 설명 삽입

프로그램이 정확하고 성공적으로 실행됩니다.

2.1.4 테일 삭제

꼬리 삭제 작업은 꼬리 삽입 작업과 비슷하지만 차이점은 끝에서 두 번째 노드로 이동하고 끝에서 두 번째 노드의 다음 노드를 통해 마지막 노드를 해제해야 꼬리 삭제 작업을 수행할 수 있기 때문입니다. 변경되더라도 여전히 2단계 포인터를 전달합니다.
여기에 이미지 설명 삽입

void SeqListPopBack(SeNode** phead)
{
	assert(phead);//判断指针是否为空
	assert(*phead);//判断指针指向的内容是否为空
	if ((*phead)->next == NULL)//如果只有一个节点,直接释放
	{
		free(*phead);
		*phead = NULL;
	}
	else
	{
		SeNode* tail = *phead;
		while (tail->next->next != NULL)//如果多个,遍历到末尾的前一个释放
		{
			tail = tail->next;
		}
		free(tail->next);
		tail->next = NULL;                       
	}
}

여기에 이미지 설명 삽입

2.1.5 플러그

헤더를 삽입할 때 주의해야 할 점은 포인터의 동작인데, 노드가 여러 개인 경우에는 헤더를 삽입하면서 원래 노드를 어떻게 링크할지 고려해야 합니다.

노드에서는 할당을 직접 교체하는 것으로 충분합니다.

여기에 이미지 설명 삽입

void SeqListPushFront(SeNode** phead, SeDataType x)
{
	assert(phead);//判断指针是否为空
	SeNode* newnode = BuySeqListNode(x);//创建新节点
	if (*phead == NULL)//当只有一个节点的时候,直接赋值
	{
		*phead = newnode;
	}
	else//多个节点是先链接在赋值
	{
		newnode->next = *phead;
		*phead = newnode;
	}
}

여기에 이미지 설명 삽입

성공적으로 실행

2.1.6 헤더 삭제

헤드 삭제는 사진 바로 위에 있는 헤드 삽입과 유사합니다.
여기에 이미지 설명 삽입

//头删
void SeqListPopFront(SeNode** phead)
{
	assert(phead);//判断指针是否为空
	assert(*phead);//判断指针指向的内容是否为空
	if ((*phead)->next == NULL)//只有一个节点时,直接释放
	{
		free(*phead);
		*phead = NULL;
	}
	else//多个节点时,记录新头结点,释放原头结点
	{	
		SeNode* pphead = (*phead)->next;
		free(*phead);
		*phead = pphead;
	}
}

여기에 이미지 설명 삽입

성공적으로 실행

2.1.7 검색

이 인터페이스의 원리는 인쇄 인터페이스의 원리와 동일하며 링크드 리스트를 순회하지만 차이점은 이 함수가 주소를 반환한다는 것입니다.

SeNode* SeqListFind(SeNode* phead,SeDataType x)
{
	assert(phead);//判断指针是否为空
	SeNode* cur = phead;//赋值遍历
	while (cur->next != NULL)
	{
		if (cur->data == x)//判断相等
		{
			return cur;//相等返回地址
		}
		cur = cur->next;
	}
	return NULL;	//不等返回NULL;
}

여기에 이미지 설명 삽입

2.1.8 pos 위치 뒤에 데이터 삽입

여기에 이미지 설명 삽입

void SeqListInsterAfter(SeNode**phead,SeNode* pos, SeDataType x)
{
	assert(pos);//判断指针是否为空
	assert(*phead);
	if (pos == *phead)//如果相等,就是头插
	{
		SeqListPushFront(phead,x);
	}
	else
	{
		SeNode* node = BuySeqListNode(x);
		SeNode* pphead = pos->next;//记录原结点链接的数据
		node->next = pphead;//链接
		pos->next = node;
	}
}

여기에 이미지 설명 삽입

성공적으로 수정됨

2.1.9 위치 삭제

여기에 이미지 설명 삽입

void SeqListEraseAfter(SeNode** phead, SeNode* pos)
{
	assert(pos);//判断空指针
	if (pos==*phead)//如果相等就是头删
	{
		SeqListPopFront(phead);
	}
	else
	{
		SeNode* cur = *phead;
		while (cur->next != pos)//记录pos的前一个位置
		{
			cur = cur->next;
		}
		cur->next = pos->next;//链接
		free(pos);//释放
		pos = NULL;
	}
}

전체 코드

//SeqList.h
#define _CRT_SECURE_NO_WARNINGS 1
#pragma once
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
#include<malloc.h>
typedef int SeDataType;
//定义结点
typedef struct SeqList
{
	SeDataType data;
	struct SeqList* next;
}SeNode;
//初始化
void SeqListInit(SeNode** phead);
//销毁
void SeqListDestroy(SeNode** phead);
//打印
void SeqListPrint(SeNode* phead);
//尾插
void SeqListPushBack(SeNode** phead, SeDataType x);
//尾删
void SeqListPopBack(SeNode** phead);
//头插
void SeqListPushFront(SeNode** phead, SeDataType x);
//头删
void SeqListPopFront(SeNode** phead);
//pos位置之后插入
void SeqListInsterAfter(SeNode** phead,SeNode* pos, SeDataType x);
//删除pos位置
void SeqListEraseAfter(SeNode** phead, SeNode* pos);

//SeqList.c
#define _CRT_SECURE_NO_WARNINGS 1
#include"SeqList.h"
//初始化
SeNode* BuySeqListNode(SeDataType x)
{
	SeNode* newnode = (SeNode*)malloc(sizeof(SeNode));//创建结点
	if (newnode == NULL)//判断结点是否生成成功
	{
		perror("BuySeqListNode");
		exit(-1);
	}
	newnode->data = x;//赋值
	newnode->next = NULL;
	return newnode;//返回结点地址
}
//销毁
void SeqListDestroy(SeNode** phead)
{
	assert(phead);//判断是否为空
	while (*phead)//循环free空间
	{
		SeNode* next = (*phead)->next;//记录下一个节点,避免链接丢失
		free(*phead);//释放
		*phead = next;
	}
	*phead = NULL;//置空
}
//打印
void SeqListPrint(SeNode* phead)
{
	SeNode* cur = phead;//记录位置
	while (cur)
	{
		printf("%d->", cur->data);//打印
		cur = cur->next;//赋值遍历
	}
	printf("NULL\n");
}
//尾插
void SeqListPushBack(SeNode** phead, SeDataType x)
{
	assert(phead);//判断指针是否为空
	SeNode* node = BuySeqListNode(x);//开辟节点
	if (*phead == NULL)//如果*phead为空,代表链表为空,之间赋值替换即可
	{
		*phead = node;
	}
	else
	{
		SeNode* tail = *phead;
		while (tail->next != NULL)//如果不为空,则遍历链表,在最后一个结点进行链接
		{
			tail = tail->next;
		}
		tail->next = node;
	}
}
//尾删
void SeqListPopBack(SeNode** phead)
{
	assert(phead);//判断指针是否为空
	assert(*phead);//判断指针指向的内容是否为空
	if ((*phead)->next == NULL)//如果只有一个节点,直接释放
	{
		free(*phead);
		*phead = NULL;
	}
	else
	{
		SeNode* tail = *phead;
		while (tail->next->next != NULL)//如果多个,遍历到末尾的前一个释放
		{
			tail = tail->next;
		}
		free(tail->next);
		tail->next = NULL;                       
	}
}
//头插
void SeqListPushFront(SeNode** phead, SeDataType x)
{
	assert(phead);//判断指针是否为空
	SeNode* newnode = BuySeqListNode(x);//创建新节点
	if (*phead == NULL)//当只有一个节点的时候,直接赋值
	{
		*phead = newnode;
	}
	else//多个节点是先链接在赋值
	{
		newnode->next = *phead;
		*phead = newnode;
	}
}
//头删
void SeqListPopFront(SeNode** phead)
{
	assert(phead);//判断指针是否为空
	assert(*phead);//判断指针指向的内容是否为空
	if ((*phead)->next == NULL)//只有一个节点时,直接释放
	{
		free(*phead);
		*phead = NULL;
	}
	else//多个节点时,记录新头结点,释放原头结点
	{	
		SeNode* pphead = (*phead)->next;
		free(*phead);
		*phead = pphead;
	}
}
SeNode* SeqListFind(SeNode* phead,SeDataType x)
{
	assert(phead);//判断指针是否为空
	SeNode* cur = phead;//赋值遍历
	while (cur->next != NULL)
	{
		if (cur->data == x)//判断相等
		{
			return cur;//相等返回地址
		}
		cur = cur->next;
	}
	return NULL;	//不等返回NULL;
}
//pos位置之后插入
void SeqListInsterAfter(SeNode**phead,SeNode* pos, SeDataType x)
{
	assert(pos);//判断指针是否为空
	assert(*phead);
	if (pos == *phead)//如果相等,就是头插
	{
		SeqListPushFront(phead,x);
	}
	else
	{
		SeNode* node = BuySeqListNode(x);
		SeNode* pphead = pos->next;//记录原结点链接的数据
		node->next = pphead;//链接
		pos->next = node;
	}
}
//删除pos位置
void SeqListEraseAfter(SeNode** phead, SeNode* pos)
{
	assert(pos);//判断空指针
	if (pos==*phead)//如果相等就是头删
	{
		SeqListPopFront(phead);
	}
	else
	{
		SeNode* cur = *phead;
		while (cur->next != pos)//记录pos的前一个位置
		{
			cur = cur->next;
		}
		cur->next = pos->next;//链接
		free(pos);//释放
		pos = NULL;
	}
}


//SeqListTest.c
#define _CRT_SECURE_NO_WARNINGS 1
#include"SeqList.h"
void SeqListTest()
{
	SeNode* plist = NULL;
	SeqListPushFront(&plist, 5);
	SeqListPushFront(&plist, 4);
	SeqListPushFront(&plist, 3);
	SeqListPushFront(&plist, 2);
	SeqListPushFront(&plist, 1);
	SeqListPushFront(&plist, 0);
	SeqListPrint(plist);
	SeNode* ptr = SeqListFind(plist, 3);
	if (ptr != NULL)
	{
		SeqListEraseAfter(&plist, ptr);
	}
	else
	{
		printf("数据有误\n");
	}
	SeqListPrint(plist);
	/*SeNode* ptr = SeqListFind(plist, 3);
	if (ptr != NULL)
	{
		printf("%p %d", ptr,ptr->data);
	}
	else
	{
		printf("数据有误\n");
	}*/
	//SeqListPopFront(&plist);
	//SeqListPopFront(&plist);
	//SeqListPopFront(&plist);
	//SeqListPopFront(&plist);
	//SeqListPopFront(&plist);
	//SeqListPrint(plist);
	//SeqListPopFront(&plist);
	//SeqListPrint(plist);
	/*SeqListPushBack(&plist, 1);
	SeqListPushBack(&plist, 2);
	SeqListPushBack(&plist, 3);
	SeqListPushBack(&plist, 4);
	SeqListPushBack(&plist, 5);
	SeqListPrint(plist);
	SeqListPopBack(&plist);
	SeqListPrint(plist);
	SeqListPopBack(&plist);
	SeqListPrint(plist);
	SeqListPopBack(&plist);
	SeqListPrint(plist);
	SeqListPopBack(&plist);
	SeqListPrint(plist);
	SeqListPopBack(&plist);
	SeqListPrint(plist);
	SeqListDestroy(&plist);*/

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

ont(&plist);

//SeqListPrint(plist);
/ SeqListPushBack(&plist, 1);
SeqListPushBack(&plist, 2);
SeqListPushBack(&plist, 3);
SeqListPushBack(&plist, 4);
SeqListPushBack(&plist, 5);
SeqListPrint(plist);
SeqListPopBack(&plist);
SeqListPrint(plist);
SeqListPopBack(&plist);
SeqListPrint(plist);
SeqListPopBack(&plist);
SeqListPrint(plist);
SeqListPopBack(&plist);
SeqListPrint(plist);
SeqListPopBack(&plist);
SeqListPrint(plist);
SeqListDestroy(&plist);
/

}
int main()
{ SeqListTest(); 0을 반환합니다. }




추천

출처blog.csdn.net/nanmiao666/article/details/131946638