이 블로그는 주로 시퀀스 테이블과 리드 노드 연결 목록 (단일 연결 목록, 단일 순환 연결 목록, 이중 원형 연결 목록)의 구현에 관한 것입니다.
코드에 대한 링크는 헤드 노드없이 링크 된 목록의 끝에 첨부됩니다.
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;
}
시퀀스 테이블의 장점 :
- 구현하기 쉬움
- 랜덤 액세스
용량보다 더 많은 공간이 필요하면 어떻게합니까?
다음과 같이 동적 개발을 사용하여 공간을 다시 열 수 있습니다.
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);//释放
}
}
이중 순환 연결 목록의 구현은 매우 편리하지만 구조가 가장 복잡합니다.
필요한 경우 여기에서 코드를 다운로드 할 수 있습니다 (연결된 목록에는 헤드리스 노드가 있습니다) : 코드
요약 : 시퀀스 테이블에서 구현하는 것이 매우 편리하지만 용량이 제한되어 있습니다. 더 많은 공간이 열리면 공간 낭비가 발생합니다. 연결 목록에는 헤드리스 노드 연결 목록, 단일, 단일 루프, 이중 및 이중 루프 등 많은 구조가 있습니다. 일반적으로 사용되는 연결 목록은 헤드리스 노드의 단일 연결 목록과 선행 노드의 이중 루프 연결 목록입니다.