Loading

数据结构_线性表之链表(1)

这是链表的第一篇。链表方便插入删除,但是不是随机存取。实际应用中,用于频繁的增加删除操作。

1.基础知识。

 

 

单链表:

<1>带头结点单链表

L为头指针,指向第一个结点,当有头结点时,第一个结点为头结点;当没有头结点时,第一个结点为存储第一个元素的a1结点

 

 <2>不带头结点的单链表

 

 两者区别:通常使用有头结点的链表,因为有头结点比较方便。因为对于类似头插法这样的操作,需要对第一个存储元素的结点进行操作时,很明显带有头结点的头指针可以保持不变,因为元素操作都在头结点与a1结点之间完成;而对于没有头结点的链表,需要频繁改变头指针的位置,才能保持头指针始终指向第一个结点。

 

2.基本操作

单链表模块

<1>头插法建立单链表:总是在头部操作,进行插值

 

 代码:

 typedef struct Node{
      int data;
      struct Node *next;
 }Node;
//头插法创建单链表,插入的元素总在头结点之后
Node *CreateList_L(Node* &L,int n){//前提L已经初始化完成 
    Node *p;    int num;//p保存结点,num保存数据
    L = (Node*)malloc(sizeof(Node));//创建头结点
    L->next = NULL;
    for(int i=0;i<n;i++){
        p = (Node*)malloc(sizeof(Node));//创建新的结点
        cin>>num;
        p->data = num;//存入结点数据域
        p->next = L->next;//p指向下一个 
        L->next = p;//再将p给单链表L的表头 
    }
    return L; //返回值为Node*型
}

<2>尾插法建立单链表:总是在尾部操作,进行插值

尾插法需要增加一个尾指针,尾指针总是用于指向最后一个尾结点

第一步:

 

 第二步:

 

 以此类推,每一次r都往后移动。

代码:

 typedef struct Node{
      int data;
      struct Node *next;
 }Node;
//尾插法创建单链表,插入的元素总在最后一个位置
Node *CreateList_L(Node* &L,int n){//前提L已经初始化完成 
    Node *p,*r;    int num;//p保存结点,num保存数据,r用于指向最后一个结点
    L = (Node*)malloc(sizeof(Node));//创建头结点
    L->next = NULL;
r = L;//初始时头尾指针指向同一个结点
for(int i=0;i<n;i++){ p = (Node*)malloc(sizeof(Node));//创建新的结点 cin>>num; p->data = num;//存入结点数据域 p->next = r->next;//p指向的下一个位置为NULL r->next = p;//r指针后移,指向尾结点
r = p;
} return L; //返回值为Node*型 }

 <3>按序号查找结点值

//单链表结构 
typedef struct LNODE
{
    int data;
    struct LNODE *next;
}node;
 
node *FindById(Lnode *L,int id)
{
    Lnode *p = L->next;
    int j=1;
        if(i==0)
             return L;//i=0返回头结点
        if(i<1)
             return NULL;//i不合法,返回NULL:
    while(p!=NULL&&j<id)
    {
        p=p->next;
        j++;
    }
        return p;//返回第id个结点的指针,若id大于表长,则返回NULL;
}
 

<4>按值查找

node *FindByValue(Lnode *L,int num)
{
    Lnode *p = L->next;
    while(p!=NULL&&p->data!=num)
    {
        p=p->next;
    }
        return p;//找到则返回找到的结点的指针,否则返回NULL;
}

插入结点操作:

1、p=FindById(L,id-1);//查找要插入位置的前驱结点
2、s->next=p->next;//s为要插入的结点
3、p->next=s;

删除结点操作:

1、p=FindById(L,i-1);//查找要删除位置的前驱结点
2、q=p->next;//令q指向要删除的结点
3、p->next=q->next;//将结点*q断开
4free(q);//释放q内存

获取单链表长度

有头结点时:

int GetLength(Lnode *L)
{
    Lnode *p = L->next;
    int i=0;
    while(p!=NULL&&p->data!=num)
    {
        p=p->next;
        i++;
    }
        return i;//返回长度;
}

无头结点时:

int GetLength(Lnode *L)
{
    Lnode *p = L;//与有头结点的区别
    int i=0;
    while(p!=NULL&&p->data!=num)
    {
        p=p->next;
        i++;
    }
        return i;//返回长度;
}

<5>单链表逆置

 

 

 

 

 重复下面操作

 

 

 

 代码

 

 

双链表模块

 

//双链表结构 
typedef struct LNODE
{
    int data;
    struct LNODE *prior,*next;
}node;

 

 

 

 

 

 1.双向链表的插入操作:

 

 插入结点代码:

1、s->next=p->next;//将结点*s插入到*p之后
2、p->next->prior=s;
3、s->prior=p;
4、p->next=s;

语句顺序有多种,但是一定要保证第4步一定要1,2两句之后;但是我觉得上面语句最好记;因为和单链表的插入一样,都是先处理p->next指向的结点;处理完再处理其他的。

2.双链表的结点删除操作

双链表的删除比起单链表简单多了,因为单链表删除结点的时候,无法像双链表那样来回切换前后结点,因为双链表多了个prior指针域。

 

 

 

删除结点代码:

1、p->next=q->next;//将结点q链断开
2、q->next->prior=p;
3.free(q)

 上面是通过两个结点信息,一个结点信息也可以删除。

posted @ 2020-08-14 21:47  兜里还剩五块出头  阅读(289)  评论(0编辑  收藏  举报