单链表基操

linkList.c
首先通过list_create函数创建一个链表头节点,初始化其长度为 0 且next指针为空。list_emptry函数用于判断链表是否为空。list_apply函数可创建并初始化一个包含数据元素的节点。list_insert_head能在链表头部插入节点,list_insert_pos可在指定位置插入节点,list_insert_end则在链表尾部插入节点。list_del_headlist_del_poslist_del_end分别实现了删除链表头部、指定位置和尾部节点的功能。list_find_node用于查找指定位置的节点,list_find_value可查找特定数据值所在的位置。list_updata_poslist_updata_value分别能根据位置和数据值来更新节点数据。list_reverce函数用于反转链表,list_unique可去除链表中的重复元素,list_sort实现了对链表的排序操作,list_clear清空链表,list_len获取链表长度,list_destry销毁整个链表并释放内存资源,这些函数共同构建起了一个较为完整的单链表操作体系,方便在程序中对单链表进行灵活的处理与操作。

#include"linkList.h"
#include<myhead.h>
// 定义LEN宏,方便后续使用链表长度,这里相当于取链表L的长度成员len
#define LEN L->len 

// 创建一个链表头节点,分配内存空间并初始化长度为0,next指针指向NULL,最后返回该头节点指针
Node_ptr list_create(){
    Node_ptr p=(Node_ptr)malloc(sizeof(Node));
    p->len=0;
    p->next=NULL;
    return p;
}

// 判断链表是否为空,如果传入的链表指针L为NULL,则返回 -1表示参数错误;
// 如果L的next指针为NULL,说明链表为空,返回1,否则返回0
int list_emptry(Node_ptr L){
    if(NULL==L){
        return -1;
    }
    return NULL==L->next;
}

// 创建一个新节点,先调用list_create创建节点,若创建失败(返回NULL)则直接返回NULL,
// 否则将传入的数据元素e赋值给节点的数据成员data,并将next指针设为NULL,最后返回新节点指针
Node_ptr list_apply(datatype e){
    Node_ptr p=list_create();
    if(NULL==p){
        return NULL;
    }
    p->data=e;
    p->next=NULL;
    return p;
}

// 在链表头部插入节点,若链表指针L为NULL,返回 -1表示插入失败;
// 先创建一个新节点存放数据元素e,然后将新节点的next指针指向原链表头节点(L->next),
// 再让链表头节点指针指向新节点,最后链表长度加1,并返回1表示插入成功
int list_insert_head(Node_ptr L,datatype e){
    if(NULL==L){
        return -1;
    }
    Node_ptr p=list_apply(e);
    p->next=L->next;
    L->next=p;
    L->len++;
    return 1;
}

// 在链表中查找指定位置pos的节点,若链表指针L为NULL,返回NULL;
// 若链表为空或者传入的位置pos不合法(小于等于0或者大于链表长度LEN),输出提示信息并返回NULL;
// 否则通过循环遍历链表找到指定位置的节点并返回该节点指针
Node_ptr list_find_node(Node_ptr L,int pos){
    if(NULL==L){
        return NULL;
    }
    if(list_emptry(L)||pos<=0||pos>LEN){
        puts("位置不合法");
        return NULL;
    }
    Node_ptr p=L;
    for(int i=0;i<pos;i++){
        p=p->next;
    }
    return p;

}

// 展示链表中的所有元素,如果链表指针L为NULL,直接返回;
// 若链表为空,输出提示信息“为空”;
// 否则通过循环遍历链表的每个节点(从第一个有效节点L->next开始),输出节点的数据元素
void list_show(Node_ptr L){
    if(NULL==L){
        return;
    }
    if(list_emptry(L)){
        puts("为空");
    }
    for(Node_ptr p=L->next;p!=NULL;p=p->next){
        printf("%d ",p->data);

    }
    puts("");
}

// 在链表指定位置pos插入节点,若链表指针L为NULL,返回 -1表示插入失败;
// 若链表为空或者传入的位置pos不合法(小于0或者大于链表长度LEN),输出错误提示信息;
// 先创建一个新节点存放数据元素e,再找到pos - 1位置的节点,将新节点插入到该位置后面,最后链表长度加1,并返回1表示插入成功
// (这里注意代码中修改链表长度的方式LEN++可能有问题,应该是L->len++才对,宏定义只是简单文本替换)
int list_insert_pos(Node_ptr L,int pos,datatype e){
    if(NULL==L) return -1;
    if(list_emptry(L)||pos<0||pos>LEN){
        puts("list_insert_pos:错误");
    }
    Node_ptr newp=list_apply(e);
    Node_ptr p=list_find_node(L,pos - 1);
    newp->next=p->next;
    p->next=newp;
    LEN++;
    return 1;
}

// 在链表尾部插入节点,若链表指针L为NULL,返回 -1表示插入失败;
// 先创建一个新节点存放数据元素e,然后找到链表末尾节点(通过调用list_find_node查找LEN位置的节点),
// 将新节点接到末尾节点后面,最后链表长度加1,并返回0(这里返回值可能不太符合常规意义上的表示插入成功与否,一般也返回1较好)
int list_insert_end(Node_ptr L,datatype e){
    if(NULL==L) return -1;
    Node_ptr ep=list_apply(e);
    Node_ptr p=list_find_node(L,LEN);
    p->next=ep;
    ep->next=NULL;
    LEN++;
    return 0;
}

// 删除链表头部节点,若链表指针L为NULL,返回 -1表示删除失败;
// 若链表为空,输出提示信息“为空”,并返回0表示无节点可删;
// 否则记录要删除的头节点(L->next),将链表头节点指针指向原头节点的下一个节点,释放要删除的节点内存,链表长度减1,并返回1表示删除成功
int list_del_head(Node_ptr L){
    if(NULL==L){
        return -1;
    }
    if(list_emptry(L)){
        puts("为空");
        return 0;
    }
    Node_ptr delp=L->next;
    L->next=delp->next;
    free(delp);
    LEN--;
    return 1;
}

// 删除链表指定位置pos的节点,若链表指针L为NULL,返回 -1表示删除失败;
// 先找到pos - 1位置的节点,记录要删除的节点(该节点的下一个节点),
// 然后调整指针跳过要删除的节点,释放该节点内存,链表长度减1,最后返回当前链表长度LEN
int list_del_pos(Node_ptr L,int pos){
    if(NULL==L) return -1;
    Node_ptr p=list_find_node(L,pos - 1);
    Node_ptr delp=p->next;
    p->next=delp->next;
    free(delp);
    LEN--;
    return LEN;
}

// 删除链表尾部节点,若链表指针L为NULL,返回 -1表示删除失败;
// 先找到倒数第二个节点(通过查找LEN - 1位置的节点),记录要删除的尾节点(该节点的下一个节点),
// 然后调整指针跳过要删除的节点,释放该节点内存,链表长度减1,最后返回当前链表长度LEN
int list_del_end(Node_ptr L){
    if(NULL==L) return -1;
    Node_ptr p=list_find_node(L,LEN - 1);
    Node_ptr delp=p->next;
    p->next=delp->next;
    free(delp);
    LEN--;
    return LEN;
}

// 在链表中查找特定数据值e所在的位置,若链表指针L为NULL,返回 -1表示查找失败;
// 若链表为空,输出错误提示信息;
// 否则通过循环遍历链表每个节点,若找到数据值相等的节点,返回该节点在链表中的序号(从1开始计数)
int list_find_value(Node_ptr L,datatype e){
    if(NULL==L) return -1;
    if(list_emptry(L)) {
        puts("list_find_value:err");
    }
    int flag = 1;
    for(Node_ptr i=L->next;i!=NULL;i=i->next){
        if(i->data==e){
            return flag;
        }
        flag++;
    }
}

// 根据指定位置pos更新节点的数据元素为e,若链表指针L为NULL,返回 -1表示更新失败;
// 先找到指定位置pos的节点,然后将该节点的数据成员赋值为e,最后返回1表示更新成功
int list_updata_pos(Node_ptr L,int pos,datatype e){
    if(NULL==L) return -1;
    Node_ptr p=list_find_node(L,pos);
    p->data=e;
    return 1;
}

// 根据指定的数据值val来更新链表中第一个匹配节点的数据元素为e,若链表指针L为NULL,返回 -1表示更新失败;
// 若链表为空,返回0表示无节点可更新;
// 否则通过循环遍历链表每个节点,若找到数据值等于val的节点,将其数据元素更新为e,并返回1表示更新成功
int list_updata_value(Node_ptr L,datatype val,datatype e){
    if(NULL==L) return -1;
    if(list_emptry(L)) return 0;
    for(Node_ptr i=L->next;i!=NULL;i=i->next){
        if(i->data==val){
            i->data=e;
            return 1;
        }
    }
    return 0;
}

// 反转链表,若链表指针L为NULL或者链表为空,返回 -1表示反转失败;
// 先记录原链表第一个有效节点(L->next)为H,将链表头节点的next指针设为NULL,
// 然后通过循环,每次取出原链表的下一个节点(p = H->next,这里代码重复取了H->next,应该是个小失误,不过不影响理解逻辑),
// 将取出的节点插入到链表头部(通过调整指针指向),实现链表反转,最后返回1表示反转成功
int list_reverce(Node_ptr L){
    if(NULL==L||list_emptry(L)){
        return -1;
    }
    Node_ptr H=L->next;
    L->next=NULL;
    
    while(H!=NULL){
        Node_ptr p=H->next;
        H=H->next;
        p->next=L->next;
        L->next=p;
        
    }
    return 1;
}

// 去除链表中的重复元素,通过两层循环遍历链表,外层循环指针p依次指向每个节点,
// 内层循环从p的下一个节点开始(j = p),如果发现有节点的数据元素与p的数据元素相同,
// 则删除该重复节点(记录要删除的节点delj,调整指针跳过该节点并释放内存),否则继续往后遍历内层循环
int list_unique(Node_ptr L){
    Node_ptr p=L->next;
    while(p!=NULL){
        Node_ptr j=p;
        while(j->next!=NULL){
            if(p->data==j->next->data){
                Node_ptr delj=j->next;
                j->next=delj->next;
                free(delj);
            }else{
                j=j->next;
            }
        }
        p=p->next;
    }
}

// 对链表进行排序,根据传入的参数n决定排序方式,若n为1,采用简单的冒泡排序思想,
// 通过两层循环遍历链表,外层循环控制比较轮数(从第1轮开始,共LEN - 1轮),
// 内层循环用于比较相邻节点的数据元素大小,若发现逆序则交换数据元素;
// 若n不为1,采用插入排序思想(代码可能不太完整规范,比如部分变量的命名不太清晰等,但大致思路是这样),
// 先记录已排序部分的最后一个节点lastSort和待插入节点curr,然后比较curr与已排序部分节点的数据大小,
// 如果curr大于等于lastSort的数据,则curr往后移动;
// 否则,找到合适的插入位置(通过循环比较数据大小确定p的位置),将curr插入到该位置,然后更新curr继续往后处理
int list_sort(Node_ptr L,int n){
    if(n==1){
        for(int i=1;i<LEN;i++){
            for(Node_ptr p=L->next;p->next!=NULL;p=p->next){
                if(p->data<p->next->data){
                    datatype temp =p->data;
                    p->data=p->next->data;
                    p->next->data=temp;
                }
            }
        }
    }else{
        Node_ptr lastSort=L->next;
        Node_ptr curr=lastSort->next;
        while(curr!=NULL){
            if(lastSort->data<=curr->data){
                lastSort=lastSort->next;
            }else{
                Node_ptr p=L;
                while(p->next->data<curr->data){
                    p=p->next;
                }

                lastSort->next=lastSort->next->next;
                curr->next=p->next;
                p->next=curr;
            }
            curr=lastSort->next;
        }
    }
    
}

// 清空链表,通过不断调用list_del_head函数删除链表头部节点,直到链表为空(即L->next为NULL),最后返回1表示清空成功
int list_clear(Node_ptr L){
    while(L->next!=NULL){
        list_del_head(L);
        LEN--;
    }
    return 1;
}

// 获取链表长度,通过循环遍历链表,从链表头节点开始(p = L),统计有效节点个数(next指针不为NULL的节点),最后返回长度值
int list_len(Node_ptr L){
    Node_ptr p=L;
    int count=0;
    while(p->next!=NULL){
        count++;
        p=p->next;
    }
    return count;
}

// 销毁链表,先调用list_clear函数清空链表,然后释放链表头节点的内存空间,最后返回1表示销毁成功
int list_destry(Node_ptr L){
    list_clear(L);
    free(L);
    return 1;
}

main.c

#include<myhead.h>
#include"linkList.h"
#define S list_show(L)
int main(int argc, const char *argv[])
{
	Node_ptr L=list_create();
	list_insert_head(L,1);
	list_insert_head(L,2);
	
	list_show(L);
	puts("--2.7");
	list_insert_pos(L,2,2727);
	list_show(L);
	puts("--2.8");
	list_insert_end(L,99);
	list_show(L);
	puts("--2.9");
	list_del_head(L);
	S;
	puts("--2.10");
	list_del_pos(L,2);
	S;
	puts("--2.11");
	list_del_end(L);
	S;
	puts("--2.12");
	printf("%d\n",list_find_value(L,2727));
	
	list_insert_end(L,1);	
	list_insert_end(L,2);						  			
	list_insert_end(L,3);	
	list_insert_end(L,6);		
	list_insert_end(L,3);	
	list_insert_end(L,1);
	list_insert_end(L,1);
	list_insert_end(L,2);
	list_insert_end(L,1);
	S;
	list_unique(L);
	S;
	list_sort(L,1);
	S;
	list_sort(L,2);
	S;
	list_clear(L);
	S;
	printf("长度:%d\n",list_len(L));
	if(list_destry(L)) puts("destry:success");
	return 0;
}

linkList.h

#ifndef LINKLIST_H
#define LINKLIST_H
typedef int datatype;
typedef struct Node{
	union{
		int len;
		datatype data;
	};
	struct Node *next;
}Node,*Node_ptr;
//创建单向链表
Node_ptr list_create();
//链表为空
int list_emptry(Node_ptr L);
//申请节点封装函数
Node_ptr list_apply(datatype);
//头插
int list_insert_head(Node_ptr,datatype);
//位置查找
Node_ptr list_find_node(Node_ptr,int);
//展示
void list_show(Node_ptr);
//2.7 单向链表的任意位置插入
int list_insert_pos(Node_ptr,int,datatype);

//2.8 单向链表尾插
int list_insert_end(Node_ptr,datatype);
//2.9 单向链表头删操作
int list_del_head(Node_ptr);
//2.10 任意位置删除
int list_del_pos(Node_ptr,int);
//2.11 单向链表的尾删
int list_del_end(Node_ptr);
//2.12 单向链表按值查找返回位置
int list_find_value(Node_ptr,datatype val);
//2.13 单向链表按位置进行修改
int list_updata_pos(Node_ptr,int ,datatype);
//2.14 单向链表按值修改
int list_updata_value(Node_ptr,datatype,datatype);
//2.15 单向链表翻转
int list_reverce(Node_ptr);
//2.16 单向链表的排序
int list_sort(Node_ptr,int );
//2.17 单向链表的去重
int list_unique(Node_ptr);
//2.18 清空单向链表
int list_clear(Node_ptr);
//2.19 返回单向链表的长度
int list_len(Node_ptr);
//2.20 销毁单向链表
int list_destry(Node_ptr);
#endif

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值