このブログは主にシーケンステーブルとリードノードリンクリスト(単一リンクリスト、単一循環リンクリスト、二重循環リンクリスト)の実現についてです。
コードへのリンクは、ヘッドノードなしでリンクリストの最後にも添付されます。
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--;//元素个数减小
}
シーケンステーブルの削除:基本的な考え方は、次のデータを1つずつ上書きして、最終的に数を減らすことです。
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つのスペースの容量を超えるだけでよい場合、多くのスペースが無駄になります。
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の値を1つずつ判断します。また、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的节点
}
単一リンクリストでは、ヘッドノードの変換は考慮されないため、実装はより簡単です。ヘッドノードがない場合は、最初のノードの変更をソートの反転で考慮する必要があります。また、リンクされたリストにランダムにアクセスすることはできません。
3つの単一の循環リンクリスト:
これは、単一リンクリストに似ていますが、テールノードのポイントは空ではありませんが、ヘッドノードを再び指します。
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;
}
}
単一の循環リンクリストでは、最後のノードをループバックする必要があることに注意してください。そうしないと、エラーが発生します。
4つの二重循環リンクリスト:
二重循環リンクリストでは、各ノードに2つのポインター(1つは先行、1つは後続)があります。
ヘッドノードを持つ二重循環リンクリスト:
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
}
先行の2つのポインタと二重循環リンクリストの後続の2つのポインタがあるため、実装が特に便利です。ノードを知っていれば、ノードの先行と後続を見つけることができ、挿入、削除、および並べ替えで適切に実装されます。
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;
}
}
このブロックの順序は値の挿入と同じです。最初の2つが切断され、その後に続くシーケンス値のサイズが比較されてから、挿入が実行されます。
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);//释放
}
}
二重循環リンクリストの実現は非常に便利ですが、構造が最も複雑です。
必要に応じて、ここからコードをダウンロードできます(リンクされたリストにはヘッドレスノードがあります)。コードの
概要:シーケンステーブルに実装すると非常に便利ですが、容量が制限されます。空き容量が増えると、スペースが無駄になります。リンクリストには、ヘッドレスノードリンクリスト、シングル、シングルループ、ダブル、ダブルループなど、多くの構造があります。より一般的に使用されるリンクリストは、ヘッドレスノードのシングルリンクリストと先行ノードのダブルループリンクリストです。