【数据结构】 单链表的基本算法实现与应用

// 2.3线性表的链式存储结构————链表
/*
单链表(线性单向链接表):每个结点中除包含数据域以外只设置一个指针域,用于指向其后继节点。
双链表(线性双向链接表):每个结点中除包含数据域以外设置两个指针域,分别用于指向其前驱结点和后继结点。
头指针:头结点的指针。
首指针:指向首结点或者开始结点的指针。
尾指针:指向尾结点的指针。
从一个链表的头指针所指的头结点出发,沿着结点的链(即指针域的值)可以访问到每个结点,当进行插入删除操作时,只需修改指针域。
顺序表是线性表的直接映射,具有随机存取特性;链表不具有该特性。
顺序表的存储密度高(存储密度=结点中数据元素所占的存储量/结点所占的存储量),一般情况下,存储密度越大,存储空间的利用率越高。
顺序表的存储密度为1,链表小于1,单链表为50%。
在单链表中增加一个头结点的优点:
①单链表中首结点的插入与删除操作同其他结点一致,无需进行特殊处理。
②无论单链表是否为空都有一个头结点,统一了空表与非空表的处理过程。
*/
typedef int ElemType;
typedef struct LNode
{
    ElemType data;// 存放元素值
    struct LNode * next;// 指向后继结点
} LinkNode;// 单链表结点类型
/*
①单链表的插入结点操作:
在单链表的两个数据域分别为a和b的结点(已知a结点的指针p)之间插入一个数据域为x的结点(由s指向它)
s->next = p->next;
p->next = s;
顺序不能颠倒,因为先p->next = s;指向b的指针就不存在了,s->next = p->next = s会出现插入错误。
②单链表的删除结点操作:
删除结点p的后继结点:
p->next = p->next->next;
删除结点后释放其存储空间:
q = p->next;
p->next = q->next;
free(q);
*/

void CreateListF(LinkNode *&L, ElemType a[], int n);// 头插法建立单链表
void CreateListR(LinkNode *&L, ElemType a[], int n);// 尾插法建立单链表
void InitList(LinkNode *&L);
void DestoryList(LinkNode *&L);// 双指针法
bool ListEmpty(LinkNode *L);
int ListLength (LinkNode *L);
void DispList(LinkNode *L);
bool GetElem(LinkNode *L, int i, ElemType &e);
int LocateElem(LinkNode *L, ElemType e);
bool ListInsert(LinkNode *&L, int i, ElemType e);
bool DeteleList(LinkNode *&L, int i, ElemType &e);
void split(LinkNode *&L, LinkNode *&L1, LinkNode *&L2);// L1尾插法,L2头插法
void delmaxnode(LinkNode *&L);// 双指针法
void sort(LinkNode *&L);// 直接插入法


int main()
{
    LinkNode *L, *L1, *L2;
    int a[9] = {4, 8, 27, 89, -2, 7, 0, 68, 3};
    ElemType e;
    InitList(L);
    InitList(L1);
    InitList(L2);
    if (ListEmpty(L))
        printf("链表为空!\n");
    else
        printf("链表不为空!\n");
    CreateListF(L, a, 9);
        CreateListF(L1, a, 9);
            CreateListF(L2, a, 9);
    DispList(L);
    CreateListR(L, a, 9);
    DispList(L);
    if (ListEmpty(L))
        printf("链表为空!\n");
    else
        printf("链表不为空!\n");
    printf("链表的长度为:%d\n", ListLength(L));
    if (GetElem(L, 3, e))
        printf("链表第3位的元素是:%d\n", e);
    else
        printf("查找序号有误!\n");
    if (LocateElem(L, 16))
        printf("元素%d在链表的第%d位。\n", 16, LocateElem(L, 16));
    else
        printf("链表中没有该元素!\n");
    if (LocateElem(L, 27))
        printf("元素%d在链表的第%d位。\n", 27, LocateElem(L, 27));
    else
        printf("链表中没有该元素!\n");
    if (ListInsert(L, 6, 98))
        DispList(L);
    if (DeteleList(L, 6, e))
    {
        printf("被删除的元素是:%d\n", e);
        DispList(L);
    }
    delmaxnode(L);
    DispList(L);
    sort(L);
    DispList(L);
    split(L, L1, L2);
    DispList(L);
    DispList(L1);
    DispList(L2);
    DestoryList(L);
    return 0;
}


// 建立单链表
//(1)头插法:时间复杂度O为(n),单链表中数据结点的顺序与数组a中元素的顺序相反。
void CreateListF(LinkNode *&L, ElemType a[], int n)
{
    LinkNode *s;
    L = (LinkNode * )malloc(sizeof(LinkNode));
    L->next = NULL;// 创建头结点,将next置空。
    for (int i=0; i<n; i++)
    {
        s = (LinkNode * )malloc(sizeof(LinkNode));
        s -> data = a[i];// 创建数据结点。
        s -> next = L -> next;// 将结点s插入原首结点前、头结点后。
        L -> next = s;
    }
}
//(2)尾插法:时间复杂度为O(n),单链表中数据结点的顺序与数组a中元素的顺序相同,需要增加尾指针r。
void CreateListR(LinkNode *&L, ElemType a[], int n)
{
    LinkNode *s, *r;
    L = (LinkNode * )malloc(sizeof(LinkNode));
    r = L;// r始终指向尾结点,初始时指向头结点。
    for (int i=0; i<n; i++)
    {    
        s = (LinkNode * )malloc(sizeof(LinkNode));
        s -> data = a[i];
        r -> next = s;
        r = s;
    }
    r -> next = NULL;// 将尾结点的next域置为NULL。
}
// 1、初始化线性表,时间复杂度O(1)
void InitList(LinkNode *&L)
{
    L = (LinkNode * )malloc(sizeof(LinkNode));
    L -> next = NULL;
}
// 2、销毁线性表,时间复杂度O(n)
void DestoryList(LinkNode *&L)// 双指针法
{
    LinkNode * pre = L, *p = L -> next;// pre指向结点p的前驱结点
    while (p != NULL)// 遍历单链表
    {
        free(pre);// 释放pre结点
        pre = p;// pre、p同步后移一个结点
        p = pre->next;
    }
    free(pre);// 循环结束时p为NULL,pre指向尾结点,释放它。
}
// 3、判空,时间复杂度O(1)
bool ListEmpty(LinkNode *L)
{
    return (L -> next == NULL);
}
// 4、求线性表的长度,时间复杂度为O(n)
int ListLength (LinkNode *L)
{
    int n=0;
    LinkNode *p = L;// p指向头结点,n置为0
    while (p != NULL)
    {
        p = p->next;
        n++;
    }
    return n;
}
// 5、输出线性表,时间复杂度为O(n)
void DispList(LinkNode *L)
{
    LinkNode *p = L -> next;
    while (p != NULL)
    {
        printf("%d ", p -> data);
        p = p -> next;
    }
    printf("\n");
}
// 6、按序号求线性表中的元素,时间复杂度为O(n)
bool GetElem(LinkNode *L, int i, ElemType &e)
{
    LinkNode *p = L;
    int j=0;
    if (i <= 0)
        return false;
    while (j<i && p != NULL)
    {   
        j++;
        p = p -> next;
    }
    if (p == NULL)
        return false;
    else
    {    
        e = p -> data;
        return true;
    }
}
// 7、按元素值查找
int LocateElem(LinkNode *L, ElemType e)
{
    LinkNode *p = L -> next;
    int i=1;
    while (p != NULL && p -> data != e)
    {
        p = p -> next; 
        i++;
    }
    if (p == NULL)
        return 0;
    else
        return i;
}
// 8、插入数据元素,时间复杂度为O(n)
bool ListInsert(LinkNode *&L, int i, ElemType e)
{
    LinkNode *p = L, *s;
    int j=0;
    if (i <= 0)
        return false;
    while (j < i-1 && p != NULL)
    {
        j++;
        p = p -> next;
    }
    if (p == NULL)
        return false;
    else
    {
        s = (LinkNode * )malloc(sizeof(LinkNode));
        s -> data = e;
        s -> next = p -> next;
        p -> next = s;
        return true;
    }
}
// 9、删除数据元素,时间复杂的为O(n)
bool DeteleList(LinkNode *&L, int i, ElemType &e)
{
    LinkNode *p = L, *q;
    int j=0;
    if (i <= 0)
        return false;
    while (j<i-1 && p != NULL)
    {
        j++;
        p = p -> next;
    }
    if (p == NULL)
        return false;
    else
    {
        q = p -> next;
        if (q == NULL)
            return false;
        e = q -> data;
        p -> next = q -> next;
        free(q);
        return true;
    }
}
// 单链表的应用示例
// 1、将一个带头结点的单链表L拆分成两个带头结点的单链表L1和L2。
void split(LinkNode *&L, LinkNode *&L1, LinkNode *&L2)// L1尾插法,L2头插法
{
    LinkNode *p = L -> next, *q2, *r1;
    L1 = L;// L1利用L的头结点
    r1 = L1;// r1始终指向L1的尾结点
    L2 = (LinkNode * )malloc(sizeof(LinkNode));
    L2 -> next = NULL;// 创建L2的头结点
    while(p != NULL)
    {
        r1 -> next = p;
        r1 = p;
        p = p -> next;
        q2 = p -> next;
        p -> next = L2 -> next;
        L2 -> next = p;
        p = q2;
    }
    r1 -> next = NULL;
}
// 2、设计一个算法,删除一个单链表L中元素值最大的结点(假设该结点只有一个)
void delmaxnode(LinkNode *&L)// 双指针法
{
    LinkNode *p = L -> next, *pre = L, *maxp = p, *maxpre = pre;
    while(p != NULL)
    {
        if (p -> data > maxp -> data)
        {
            maxp = p;
            maxpre = pre;
        }
        pre = p; 
        p = p -> next;
    }
    maxpre -> next = maxp -> next;
    free(maxp);
}
// 3、有一个带头结点的单链表L(至少有一个数据结点),设计一个算法实现所有数据结点按data域递增排序。
void sort(LinkNode *&L)// 直接插入法
{
    LinkNode *p, *pre, *q;
    p = L -> next -> next; // p指向L的第二个数据结点
    L -> next -> next = NULL; // 构造只含一个数据结点的有序单链表。
    while (p != NULL)
    {
        q = p -> next;
        pre = L;
        while (pre -> next != NULL && pre -> next -> data < p -> data)
            pre = pre -> next;
        p -> next = pre -> next;
        pre -> next = p;
        p = q;
    }
}

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值