定义单链表

typedef int ElemType;
// 定义单链表结点结构体
typedef struct LNode{
    ElemType data; //数据域:存储结点的实际数据
    struct LNode *next; //指针域:存储下一个结点的内存地址
}LNode,*LinkList;

LNode* LinkList struct LNode* 等价,后续有使用不同方式。

实现单链表(带头结点)

头插法创建单链表

void list_head_insert(LinkList &L){  //改变L的值,使用引用&
    // 1.申请头结点空间,头指针L指向头结点
    L=(LNode*)malloc(sizeof(LNode)); 
    L->next=NULL;

    ElemType x;
    scanf("%d",&x);
    LNode *s; //指针变量:用来指向申请的新结点

    // 2.循环创建结点并执行头插操作
    while (x!=9999){
        // 2.1 为新结点申请堆内存,同时指向新结点
        s=(LNode*)malloc(sizeof(LNode)); 

        // 2.2 给新结点赋值
        s->data=x; //给新结点的数据域赋值为输入的x
        s->next=L->next; //新结点的指针域指向“头结点指向的结点”;第一次赋值时为NUll,表示最后一个结点,标志链表结束

        // 2.3 头插核心
        L->next=s; //头结点的指针域指向“新结点”,新结点成为链表第一个有效结点,完成头插操作

        scanf("%d",&x); //继续读取下一个结点数据
    }
}

重点:新结点的指针域指向“头结点指向的结点”,头结点的指针域指向“新结点”

尾插法创建单链表

void list_tail_insert(LinkList &L){ //改变L的值,使用引用&
    // 1.申请头结点空间,头指针L指向头结点
    L=(LNode*)malloc(sizeof(LNode)); 
    L->next=NULL;

    ElemType x;
    scanf("%d",&x);
    LNode *s,*r=L; //s:临时指针,指向每次新申请的结点;r:尾指针,始终指向链表最后一个结点(初始指向头结点)
    
    // 2.循环创建结点并执行尾插操作
    while (x!=9999){
        // 2.1 为新结点申请堆内存,同时指向新结点
        s=(LNode*)malloc(sizeof(LNode));
        
        // 2.2 给新结点赋值
        s->data=x; 
        r->next=s; //将新结点链接到尾结点的后面(尾结点的指针域指向新结点)
        
        // 2.3 尾插核心
        r=s; //更新尾指针:让r指向新的尾结点(即刚创建的s结点)
        
        scanf("%d",&x); //继续读取下一个结点数据
    }
    r->next=NULL; //链表创建完成,将最后一个尾结点的next赋为NULL,标志链表结束
}

重点:尾插法需要使用尾指针。核心是通过尾指针r减少遍历操作,避免每次找尾结点都遍历链表。每插入一个新结点,r就更新为这个新结点,始终保持指向链表最后一个结点。

单链表的查找

  1. 链表的第一个有效结点的位置是1,头结点(L指向的结点)的位置是0。
  2. 若遇到 “位置 0 是第一个有效结点” 的说法,大概率是不带头结点的链表 / 数组式计数。
  3. 返回的值是指针,类型是LinkList

按位置查找

LinkList GetElem(LinkList L,int SearchPos){
    int i=0;

    // 查找的位置小于0,属于无效位置,直接返回NULL
    if(SearchPos<0){
        return NULL;
    }

    // 循环遍历链表:
    // 条件1(L):当前结点不为NULL(未遍历到链表尾)
    // 条件2(i<SearchPos):未到达目标位置
    while (L && i<SearchPos){
        L=L->next;
        i++;
    }

    // 循环结束后:
    // 1. 若找到目标位置(i=SearchPos),L指向对应结点,返回L
    // 2. 若遍历到链表尾仍未到目标位置(L=NULL),返回NULL
    return L;
}

按值查找

LinkList LocateElem(LinkList L,ElemType SearchVal){
    // 循环遍历链表:条件L等价于L!=NULL,未遍历到链表尾则继续
    while (L){
        if(L->data==SearchVal){
            return L;
        }
        L=L->next;
    }
    return NULL;
}

单链表的插入

bool ListFrontInsert(LinkList L,int i,ElemType InsertVal){
    // 找到第i-1个位置的结点(插入位置的前驱结点)
    LinkList p= GetElem(L,i-1);

    // 如果前驱结点不存在(i位置不合法),插入失败
    if(p==NULL){
        return false;
    }

    LinkList s=(LinkList) malloc(sizeof (LNode));
    s->data=InsertVal;

    // 新结点的后继指向原前驱结点的后继(先连后面,防止断链)
    s->next=p->next;

    // 前驱结点的后继指向新结点,完成插入
    p->next=s;

    return true;
}

单链表的删除

删除元素(L是不会变的,不需要引用)
bool ListDelete(LinkList L,int i){
    LinkList p= GetElem(L,i-1);
    if(p==NULL){
        return false;
    }
    LinkList q= p->next;
    // 当链表有x个结点时,删除x+1个结点,出现这种异常情况时,避免程序崩溃
    if(q==NULL){
        return false;
    }
    p->next=q->next;
    free(q);
    return true;
}

打印看看

打印函数
void print_list(LNode* L){
    L=L->next; //跳过头结点,指向第一个有效结点
    while (L!=NULL){
        printf("%3d",L->data);
        L=L->next; //指针后移,指向下一个结点
    }
}
头插法
LinkList L; //定义链表头指针(结构体类型指针)
list_head_insert(L);
print_list(L);
尾插法
LinkList L; //定义链表头指针(结构体类型指针)
list_tail_insert(L);
print_list(L);
按位置查找(使用尾插法创建)
LinkList search;
search=GetElem(L,2);
if(search!=NULL){
    printf("Succeeded in searching by GetElem\n");
    printf("data:%d,netx:%p\n",search->data,search);
} else{
    printf("netx:%p\n",search);
}
按值查找(使用尾插法创建)
LinkList search;
search=LocateElem(L,12);
if(search!=NULL){
    printf("Succeeded in searching by LocateElem\n");
    printf("data:%d,netx:%p\n",search->data,search);
} else{
    printf("netx:%p\n",search);
}
链表的插入
LinkList L; //定义链表头指针(结构体类型指针)
list_tail_insert(L); 
print_list(L);
bool ret;
ret=ListFrontInsert(L,3,99);
print_list(L);
printf("ret=%s",ret ? "true" : "false");
LinkList L; //定义链表头指针(结构体类型指针)
list_tail_insert(L);
print_list(L);
ListDelete(L,4);
print_list(L);
return 0;

运行看看

头插法
1 2 3 4 78 12 45 52 9999
 52 45 12 78  4  3  2  1
尾插法
1 2 3 4 78 12 45 52 9999
  1  2  3  4 78 12 45 52
按位置查找
Succeeded in searching by GetElem
data:2,netx:0000000000BD1490
按值查找
Succeeded in searching by LocateElem
data:12,netx:0000000000BD1510
链表的插入
1 2 3 4 78 12 45 52 9999
  1  2  3  4 78 12 45 52
  1  2 99  3  4 78 12 45 52
ret=true
链表的删除
1 2 3 4 78 12 45 52 9999
  1  2  3  4 78 12 45 52
  1  2  3 78 12 45 52

标签: 数据结构, 链表

添加新评论