三、单链表的基本运算

查找运算(带头结点)
(1)按结点序号查找

在单链表中要查找第 i 个结点,就必须从链表的第1个结点(开始结点,序号为1)开始,序号为0的是头结点,p指向当前结点,j为计数器,初始值为1,当p扫描下一个结点时,计数器加1。当 j=i 时,指针 p 指向的节点就是要找的节点。

/*
    按节点序号查找
    head 为带头节点的单链表的头指针, i为要查找的节点序号
*/

ListNode * GetNodeI(LinkList head, int i){
    ListNode *p; int j;
    p = head->next; j=1;    //使P指向第一个节点,j=1
    while(p != NULL && j < i){  //指针向后查找,直到p指向第I个结点或P为空为止
        p = p->next; ++j;
    }
    if(j == i) 
        return p;
    else 
        return NULL;
}
(2)按结点值查找

单链表中按结点值查找结点,就是从链表的开始结点出发,沿着链逐个将结点的值和给定值k进行比较,相等则返回该节点的存储位置,否则返回NULL

/*
    按节点值查找
    head 为带头节点的单链表的头指针,k为要查找的节点值
*/
ListNode * GetNodeK(LinkList head, DataType k){
    ListNode * p = head->next;  //p指向开始结点
    while (p && p->data != k)   //循环到p等于NULL,或p->data等于k为止
    {
        p = p->next;            //指针指向下一个结点
    }
    return p;   //若存在值为k的节点,则p指向该节点,否则p为NULL
}
插入运算

插入运算是将值为 x 的新结点插入到表的第 i 个结点的位置上,即插入到 ai-1ai 之间。链表插入时不需要移动结点,需要移动指针进行位置查找。
思想:先使 p 指向 ai-1 的位置,然后生成一个数据域为 x 的新结点 *s,再进行插入运算

/*
    插入运算
    再第i个结点的位置上,插入一个数据域值为x的新结点
*/
void InsertList(LinkList head,int i,DataType x){
    ListNode *p, *s; int j;
    p = head; j = 0;
    while (p != NULL && j < i-1)
    {
        p = p->next; j++;   //使p指向第i-1个结点
    }
    if (p == NULL)
    {
        printf("ERROR\n");
        return;
    }
    else
    {
        s = malloc(sizeof(ListNode));   //申请新结点
        s->data = x;
        s->next = p->next;
        p->next = s;
    }
}

删除运算

删除运算就是将链表的第 i 个结点从表中删去。由于第 i 个结点的存储地址是存储在第 i-1 个结点的指针域 next 中,因此要先使 p 指向第 i-1 个结点,然后使得 p->next 指向第 i+1 个结点,再将第 i 个结点释放掉。

/*
    删除运算
    删除第i个结点
*/
DataType DeleteList(LinkList head,int i){
    ListNode *p, *s;
    DataType x; int j;
    p = head; j = 0;
    while (p != NULL && j < i-1)    //使p指向第i-1个结点
    {
        p = p->next; ++j;
    }
    if (p == NULL)
    {
        printf("为止错误\n");
        exit(0);
    }
    else
    {
        s = p->next;        //s指向第i个结点
        p->next = s->next;  //使p指向第i+1个结点
        x = s->data;    //保存被删除结点的值
        free(s);        //释放第i个结点
        return x;
    }
}
mian函数中执行
#include <stdio.h>
#include "E:/Dev/C/DataStructure/chapter2/LinkList.c"

void printLink();
int main(){
    int arr[] = {1,2,3};
    LinkList head = NULL;

    printf("尾插法建立带头结点单链表\n");
    head = CreateListR1(arr,3);

    ListNode *p = GetNodeI(head,3);
    int data = p->data;
    printf("按结点序号查找:第三个 = %d\n",data);
    ListNode *k = GetNodeK(head,2);
    printf("按结点值查找:值为2的节点 = %p\n",k);
    printf("插入运算:在第3个位置上插入值为33的结点\n");
    InsertList(head,3,33);
    printLink(head);
    int x = DeleteList(head,3);
    printf("删除运算:删除第三个位置上的值 = %d\n",x);
    printLink(head);
    return 0;
}

/*
    打印输出链表
*/
void printLink(LinkList head){
     printf("打印链表:");
    LinkList temp = head; 
    while(temp != NULL){
        // printf("%d ",temp->data);
        printf("data=%d p=%p\n",temp->data,temp);
        temp = temp->next;  
    }
    printf("\n");
}
执行结果:
尾插法建立带头结点单链表:
data=1 p=0000000000176B70
data=2 p=0000000000176B90
data=3 p=0000000000176BB0
按结点序号查找:第三个 = 3
按结点值查找:值为2的节点 = 0000000000176B90
插入运算:在第3个位置上插入值为33的结点
打印链表:data=1512480 p=0000000000176B50
data=1 p=0000000000176B70
data=2 p=0000000000176B90
data=33 p=0000000000176BD0
data=3 p=0000000000176BB0

删除运算:删除第三个位置上的值 = 33
打印链表:data=1512480 p=0000000000176B50
data=1 p=0000000000176B70
data=2 p=0000000000176B90
data=3 p=0000000000176BB0

例子

1、试写一个算法,将一个头结点指针为a的带头节点的单链表A分解成两个单链表A和B,其中头结点指针分别为a和b,使A链表中含有原链表中序号为奇数的元素,而B链表含有原链表中序号为偶数的元素,并保持原来的相对顺序。

/*
    返回新链表的头指针
 */
void Test1(LinkList a, LinkList b){

    ListNode *p, *ra, *rb;    //声明新结点和a,b的尾指针
    ra = a;
    rb = b;//尾指针指向头结点
    p = a->next;//p指向第一个节点
    int i = 1;
    while (p != NULL)
    {
        if (i % 2 == 0)
        {
            rb->next = p; 	//新结点放到尾结点后面
            rb = p; 		//尾结点指向新结点
        }
        else
        {
            ra->next = p;	//将新结点放到尾结点后面
            ra = p;			//尾结点移动到新结点
        }
        ++i;
        p = p->next;
    }
    ra->next = rb->next = NULL;
}

执行:

int main(){
    int arr[] = {1,2,3,4,5,6,7,8,9,10};
    LinkList a = CreateListR1(arr,10);
    printf("初始A:\n");
    printLink(a);
    LinkList b = (ListNode *)malloc(sizeof(ListNode));//申请B的头结点
    Test1(a, b);
    printf("分割后A:\n");
    printLink(a);
    printf("分割后B:\n");
    printLink(b);
    
    return 0;
}

结果:

初始A:
12540800 1 2 3 4 5 6 7 8 9 10
分割后A:
12540800 1 3 5 7 9
分割后B:
12522880 2 4 6 8 10

2、假设头指针为La和Lb的单链表(带头结点)分别为线性表A和B的存储结构,两个链表都是按结点数据值递增有序的。试写一算法,将这两个单链表合并为一个有序链表Lc。

/*
    合并有序链表
*/
LinkList Test2(LinkList La, LinkList Lb){
    ListNode *pc, *pa, *pb;//声明3个尾指针
    pa = La->next;
    pb = Lb->next;  //尾指针指向头结点
    // LinkList Lc = (ListNode *)malloc(sizeof(ListNode));  //声明C的头结点指针
    LinkList Lc;
    pc = Lc = La; //用A的头结点作为C的头结点。节省空间
    while (pa!= NULL && pb != NULL)
    {
        if (pa->data <= pb->data)
        {
            pc->next = pa;//将pa添加到尾指针后面
            pc = pa;    //将C的尾指针指向pa
            pa = pa->next; //下一个循环
        }else
        {
            pc->next = pb;
            pc = pb;
            pb = pb->next;
        }    
    }
    pc->next = pa==NULL? pb : pa;//拼接剩余部分
    return Lc;
}

执行:

int main(){
    int arr[] = {1,2,3,4,5,6,7,8,9,10,15,21,55};
    LinkList a = CreateListR1(arr,13);
    printf("初始A:\n");
    printLink(a);
    LinkList b = (ListNode *)malloc(sizeof(ListNode));//申请B的头结点
    Test1(a, b);
    printf("分割后A:\n");
    printLink(a);
    printf("分割后B:\n");
    printLink(b);
    
    LinkList c = Test2(a,b);
    printf("拼接后:\n");
    printLink(c);
    return 0;
}

结果:

初始A:
11230080 1 2 3 4 5 6 7 8 9 10 15 21 55
分割后A:
11230080 1 3 5 7 9 15 55
分割后B:
0 2 4 6 8 10 21
拼接后:
11230080 1 2 3 4 5 6 7 8 9 10 15 21 55
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
单链表是一种常用的数据结构,它由若干个结点组成,每个结点包含一个数据域和一个指向下一个结点的指针。单链表基本操作包括:初始化、插入结点、删除结点、查找结点等。下面是单链表基本运算算法的实现。 首先,定义单链表的结点结构体: ```c typedef struct LNode { DataType data; struct LNode *next; } LNode, *LinkList; ``` 其中,`DataType`为数据类型,可以根据实际需求进行定义。 初始化单链表: ```c void InitList(LinkList *L) { *L = (LinkList) malloc(sizeof(LNode)); (*L)->next = NULL; } ``` 插入结点: ```c void InsertList(LinkList L, int pos, DataType x) { int index = 0; LinkList p = L, q; while (p && index < pos - 1) { p = p->next; index++; } if (!p || index > pos - 1) { printf("Invalid position.\n"); return; } q = (LinkList) malloc(sizeof(LNode)); q->data = x; q->next = p->next; p->next = q; } ``` 删除结点: ```c void DeleteList(LinkList L, int pos) { int index = 0; LinkList p = L, q; while (p->next && index < pos - 1) { p = p->next; index++; } if (!p->next || index > pos - 1) { printf("Invalid position.\n"); return; } q = p->next; p->next = q->next; free(q); } ``` 查找结点: ```c LinkList FindList(LinkList L, DataType x) { LinkList p = L->next; while (p != NULL && p->data != x) { p = p->next; } return p; } ``` 需要注意的是,在插入和删除结点时,需要保证传入的位置参数`pos`合法,即在链表的范围之内。此外,当查找结点时,如果找到了指定的元素,则返回它所在的结点的指针;如果没有找到,则返回`NULL`。 完整代码如下:

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值