标题:分享:双链表的简单操作
只看楼主
renkejun1942
Rank: 14Rank: 14Rank: 14Rank: 14
来 自:不是这样
等 级:贵宾
威 望:33
帖 子:1645
专家分:5297
注 册:2016-12-1
结帖率:95.65%
 问题点数:0 回复次数:17 
分享:双链表的简单操作
这是当时写的第一版本,插入函数修改指针部分,可以很大程度的精简,但是现在这样也有好处,易读。

顺带说一个笑话:刚写这代码的时候,我给两个指针命名为left,rigth。然后left实际指向的是右边,而right实际指向的是左边。
当然郁闷了半天,结果一看,我他娘的画的图上面的指针就画反了。


程序代码:
#include <stdio.h>
#include "Doubly_Linked_List.c"//这是为了测试偷懒,实际写代码别这么干

int main( void )
{
    Node *RootP;
    Node root;
    int value;

    root.Lprev = NULL;
    root.Lnext = NULL;
    root.Value = 0;
    RootP = &root;

    while( 1 == scanf("%d",&value) )
        if ( InsertList( RootP, value ) )
            root.Value++;

    PrintList( RootP );
    printf("%d\n",root.Value);
    getchar();

    while( 1 == scanf("%d",&value) )
        if( Delete( RootP, value) )
            root.Value--;
    printf("\n");
    PrintList( RootP );
    printf("\n%d\n",root.Value);
    DelList( RootP );
    printf("\n");

    PrintList( RootP );
    printf("\n%d\n",root.Value);

    return 0;
}


程序代码:
typedef struct NODE {
    int Value;
    struct NODE *Lprev;
    struct NODE *Lnext;
}Node;
enum{ False = 0, True };
int InsertList( Node *RootP, int value );//将元素插入有序的双链表,成功返回1,否则返回0
                                         //不会插入重复的元素
void PrintList( Node *RootP );//打印双链表中的元素
int Delete( Node *RootP, int value );//删除双链表中的一个元素,成功返回1,否则返回0
void DelList( Node *RootP);//删除双链表
/*

 * 未实现查找

 */


程序代码:
#include "Doubly_Linked_List.h"
#include <stdlib.h>
#include <stdio.h>

int InsertList( Node *RootP, int value )
{
    Node *This;
    Node *Next;
    Node *NewCell;

    for( This = RootP; NULL != ( Next = This->Lprev ); This = Next )
    {
        if( value == Next->Value )
            return False;
        if( value < Next->Value )
            break;
    }
    
    NewCell = ( Node * )malloc( sizeof( Node ) );
    if( NULL == NewCell )
        return False;
    NewCell->Value = value;

    if( NULL != Next )//如果Next不为NULL,则表示不在链表尾部
    {
        if( This != RootP )//如果This 不等于RootP,则表示不在链表头部
        {
            This->Lprev = NewCell;
            Next->Lnext = NewCell;
            NewCell->Lprev = Next;
            NewCell->Lnext = This;
        }
        else
        {//如果插入点在链表头部,那么RootP应该指向新的节点
            RootP->Lprev = NewCell;
            Next->Lnext = NewCell;
            NewCell->Lprev = Next;
            NewCell->Lnext = NULL;
        }
    }
    else
    {//如果在链表尾部
        if( This != RootP )
        {//如果在链表的尾部,哑节点的Lnext应该指向新的节点
            This->Lprev = NewCell;
            RootP->Lnext = NewCell;
            NewCell->Lprev = NULL;
            NewCell->Lnext = This;
        }
        else
        {//如果是空表,哑节点应该指向新的节点
            RootP->Lprev = NewCell;
            RootP->Lnext = NewCell;
            NewCell->Lprev = NULL;
            NewCell->Lnext = NULL;
        }

    }

    return True;
}


void PrintList( Node *RootP )
{
    Node *This;

    for( This = RootP->Lprev; NULL != This; This = This->Lprev )
        printf("%d ",This->Value);
}


int Delete( Node *RootP, int value )//删除节点,需要修改两个指针,被删除的节点之前的节点的Lnext指针,以及被删除节点之后的Lperv指针。
{
    Node *This;
    Node *Next;

    for( This = RootP; NULL != ( Next = This->Lprev ) && value != Next->Value;
            This = Next )
        ;

    if( NULL == Next )
        return False;
    
    if( NULL != Next->Lprev )
    {
            This->Lprev = Next->Lprev;
            Next->Lprev->Lnext = This;
            free( Next );
    }
    else
    {
        This->Lprev = NULL;
        This->Lnext = RootP;
        free( Next );
    }

    return True;

}


void DelList( Node *RootP )
{
    Node *Temp;
    Node *This;

    This = RootP->Lprev;

    while( NULL != ( Temp = This ) )
    {
        This = Temp->Lprev;
        free( Temp );
    }

    RootP->Lprev = NULL;
    RootP->Lnext = NULL;
}


[此贴子已经被作者于2017-3-23 21:10编辑过]

搜索更多相关主题的帖子: 笑话 命名 
2017-03-23 21:03
wp231957
Rank: 16Rank: 16Rank: 16Rank: 16
来 自:神界
等 级:版主
威 望:422
帖 子:13681
专家分:53296
注 册:2012-10-18
得分:0 
标记  有功夫看

DO IT YOURSELF !
2017-03-23 21:28
wp231957
Rank: 16Rank: 16Rank: 16Rank: 16
来 自:神界
等 级:版主
威 望:422
帖 子:13681
专家分:53296
注 册:2012-10-18
得分:0 
我有点不太理解你的 “头节点不可用”
我下面的代码 就把头节点当成第一个节点(相当于数组的第一个数据)
双链表 据资料介绍 有个单独的HEAD 有个单独的TAIL 中间才是用户数据
程序代码:
#include <stdio.h>
#include <malloc.h>

typedef struct data
{
     struct data* prior;
     struct data* next;
     int value;
}tdata,*pdata;



int main(int argc, char* argv[])
{
    int s[5]={1,2,3,4,5};
    pdata phead,pn,pr,pnew;
    phead=(pdata)malloc(sizeof(tdata));
    phead->value=s[0];
    phead->next=NULL;
    phead->prior=NULL;
    pr=phead;
    pn=phead;
    for(int i=1;i<5;i++)
    {
        pnew=(pdata)malloc(sizeof(tdata));
        pnew->value=s[i];
        pn->next=pnew;
        pnew->next=NULL;
        pnew->prior=pr;
        pr=pnew;
        pn=pnew;
    }
    //正序输出
    pn=phead;
    while(pn!=NULL)
    {
        printf("%d\n",pn->value);
        pr=pn;
        pn=pn->next;
    }
    //逆序输出
    while(pr!=NULL)
    {
        printf("%d\n",pr->value);
        pr=pr->prior;
    }
    return 0;
}




DO IT YOURSELF !
2017-04-12 18:36
renkejun1942
Rank: 14Rank: 14Rank: 14Rank: 14
来 自:不是这样
等 级:贵宾
威 望:33
帖 子:1645
专家分:5297
注 册:2016-12-1
得分:0 
回复 3楼 wp231957
头节点不可用?我没说过呀。
你是不是弄错了。

双链表的头节点是一个哑节点,需要用的是它的两个指针,但是它的值不会被使用。

创建一个哑节点的目的,是为了简化向函数传递双链表,因为双链表有2个指针。

另外一个,我在主楼中的main()函数中在调用InsertList函数的时候,有一个错误的示范。

程序代码:
    Node *RootP;//这个指针是多余的。
    Node root;
    int value;

    root.Lprev = NULL;
    root.Lnext = NULL;
    root.Value = 0;
    RootP = &root;//这里也是多余的。

    while( 1 == scanf("%d",&value) )
        if ( InsertList( RootP, value ) )//这里应该写成 if( InsertList( &root, value ) )
            root.Value++;//因为哑节点的值不会被使用,所以这里我它来记录双链表中节点的数量。


[此贴子已经被作者于2017-4-12 19:17编辑过]


09:30 05/21 种下琵琶种子,能种活么?等待中……
21:50 05/27 没有发芽。
20:51 05/28 没有发芽。
23:03 05/29 没有发芽。
23:30 06/09 我有预感,要发芽了。
2017-04-12 19:11
wp231957
Rank: 16Rank: 16Rank: 16Rank: 16
来 自:神界
等 级:版主
威 望:422
帖 子:13681
专家分:53296
注 册:2012-10-18
得分:0 
那遍历时咋能知道它是用户数据还是亚节点数据

DO IT YOURSELF !
2017-04-12 19:25
renkejun1942
Rank: 14Rank: 14Rank: 14Rank: 14
来 自:不是这样
等 级:贵宾
威 望:33
帖 子:1645
专家分:5297
注 册:2016-12-1
得分:0 
回复 5楼 wp231957
不用分辨啊。
比如RootP就是传递给函数的哑节点地址。
Next是遍历指针。

Next 的初始值为 RootP->prev 这样不就避开了哑节点的值了吗?


[此贴子已经被作者于2017-4-12 19:44编辑过]


09:30 05/21 种下琵琶种子,能种活么?等待中……
21:50 05/27 没有发芽。
20:51 05/28 没有发芽。
23:03 05/29 没有发芽。
23:30 06/09 我有预感,要发芽了。
2017-04-12 19:42
wp231957
Rank: 16Rank: 16Rank: 16Rank: 16
来 自:神界
等 级:版主
威 望:422
帖 子:13681
专家分:53296
注 册:2012-10-18
得分:0 
以下是引用renkejun1942在2017-4-12 19:42:02的发言:

不用分辨啊。
比如RootP就是传递给函数的哑节点地址。
Next是遍历指针。
 
Next 的初始值为 RootP->prev 这样不就避开了哑节点的值了吗?
这个我知道  但是双链表从尾部往回遍历时 会把头节点也扫描到

DO IT YOURSELF !
2017-04-12 20:23
renkejun1942
Rank: 14Rank: 14Rank: 14Rank: 14
来 自:不是这样
等 级:贵宾
威 望:33
帖 子:1645
专家分:5297
注 册:2016-12-1
得分:0 
回复 7楼 wp231957
终止条件难道不应该就是根指针吗?否则……这遍历不就无休无止了?


[此贴子已经被作者于2017-4-12 20:32编辑过]


09:30 05/21 种下琵琶种子,能种活么?等待中……
21:50 05/27 没有发芽。
20:51 05/28 没有发芽。
23:03 05/29 没有发芽。
23:30 06/09 我有预感,要发芽了。
2017-04-12 20:30
wp231957
Rank: 16Rank: 16Rank: 16Rank: 16
来 自:神界
等 级:版主
威 望:422
帖 子:13681
专家分:53296
注 册:2012-10-18
得分:0 
//正序输出
    pn=phead;  //从前往后的指针
    while(pn!=NULL)
    {
        printf("%d\n",pn->value);
        pr=pn;
        pn=pn->next;
    }
    //逆序输出
    while(pr!=NULL)  //pr从后向前的指针  这里就会遍历到头指针里的数据
    {
        printf("%d\n",pr->value);
        pr=pr->prior;
    }

DO IT YOURSELF !
2017-04-12 20:33
renkejun1942
Rank: 14Rank: 14Rank: 14Rank: 14
来 自:不是这样
等 级:贵宾
威 望:33
帖 子:1645
专家分:5297
注 册:2016-12-1
得分:0 
回复 9楼 wp231957
for( next = RootP->next; next != Root; next = next->next )
      ;

哑节点之后的那个节点的next指针,指向的是哑节点,所以可以作为判断循环结束的条件。

如果你把一个双链表画出来会更容易理解一点儿prev和next指针为什么取这两个名字,一个前向,一个后向。

[此贴子已经被作者于2017-4-12 20:37编辑过]


09:30 05/21 种下琵琶种子,能种活么?等待中……
21:50 05/27 没有发芽。
20:51 05/28 没有发芽。
23:03 05/29 没有发芽。
23:30 06/09 我有预感,要发芽了。
2017-04-12 20:35



参与讨论请移步原网站贴子:https://bbs.bccn.net/thread-475428-1-1.html




关于我们 | 广告合作 | 编程中国 | 清除Cookies | TOP | 手机版

编程中国 版权所有,并保留所有权利。
Powered by Discuz, Processed in 0.850047 second(s), 7 queries.
Copyright©2004-2024, BCCN.NET, All Rights Reserved