数据结构链式表的实现

数据结构链式表的实现

1. 单链表

#include <cstdio>
#include <cstdlib>
#include <iostream>

#define MaxSize 100

typedef int ElemType;

using namespace std;

typedef struct LNode {
    ElemType data;
    struct LNode *next;
} LNode, *LinkList; 
// *LinkList表示人为增加的头结点,也可以用LNode *表示,LinkList是全局变量 指向头结点,
//可以这样定义:LNode *LinkList
  • 头插法建立单链表(输出时会得到反序的链表)
LinkList List_HeadInsert(LinkList &L) {     //时间复杂度为O(n)
    int x;
    LinkList s;     //LNode *s;
    L = (LinkList) malloc(sizeof(LNode));   //L = (LNode *) malloc(sizeof(LNode));  为头结点申请内存分配
    L->next = nullptr;     //初始化为空链表
    cout << "输入结点的值,输入9999表示结束:";
    cin >> x;
    while (x != 9999) {
        s = (LinkList) malloc(sizeof(LNode));
        s->data = x;
        s->next = L->next;
        L->next = s;
        cout << "输入结点的值,输入9999表示结束:";
        cin >> x;
    }
    return L;
}
  • 尾插法建立单链表(输出时会得到正序的链表)
LinkList List_TailInsert(LinkList &L) {     //时间复杂度为O(n)
    int x;
    L = (LinkList) malloc(sizeof(LNode));
    LinkList s, r = L;
    cout << "输入结点的值,输入9999表示结束:";
    cin >> x;
    while (x != 9999) {
        s = (LinkList) malloc(sizeof(LNode));
        s->data = x;
        r->next = s;
        r = s;
        cout << "输入结点的值,输入9999表示结束:";
        cin >> x;
    }
    r->next = nullptr;
    return L;
}
  • 按序号查找结点值(时间复杂度为O(n))
LinkList GetElem(LinkList L, int i) {       //因为不会对链表进行改动,所以可以不用 &L,直接用 L
    int j = 1;
    LinkList p = L->next;
    if (i == 0)     //返回头结点
        return L;
    if (i < 1)
        return nullptr;
    while (p && j < i) {    //从第一个结点出发,逐个往下查找第i个结点
        p = p->next;
        j++;
    }
    return p;   //返回第i个结点的指针,若i大于表长则返回 NULL
}
  • 按值查找表结点(时间复杂度为O(n))
LinkList LocateElem(LinkList L, ElemType e) {
    LinkList p = L->next;
    while (p->data != e && p != nullptr) {
        p = p->next;
    }
    return p;       //找到则返回该结点指针,否则返回 NULL
}
  • 在位置i上插入新值e
LinkList ListInsert(LinkList &L, int i, ElemType e) {
//法一:
// (对位置i前插操作,时间复杂度为O(n),对位置i的前驱结点后插操作,时间复杂度O(1))
//主要消耗时间在查找上
    if (i < 1)
        return nullptr;
    LinkList p = GetElem(L, i - 1);    //找到要插入的位置的前驱结点
    LinkList s;
    s = (LinkList) malloc(sizeof(LNode));   //新建一个结点放要插入的值
    s->data = e;
    s->next = p->next;  //对位置i的前驱结点后插操作
    p->next = s;
    return L;
//法二:
//可以直接把值插在位置i结点的后面,然后交换值(时间复杂度为O(1)),假设已经知道i位置的结点Q,插入其前面的结点为s
//    s->next = Q->next;
//    Q->next = s;
//    ElemType temp = Q->data;
//    Q->data = s->data;
//    s->data = temp;
}
  • 删除位置i结点操作
LinkList ListDelete(LinkList &L, int i) {
//法一:
//
    LinkList p = GetElem(L, i - 1); //寻找前驱结点
    LinkList q = p->next;   //把q指向要删除结点
    p->next = q->next;
    ElemType e = q->data;
    free(q);    //释放存储空间
    return L;
//法二:
//假设要删除Q点,可以把后继结点的值赋给自身(时间复杂度为O(1))
//    q = Q->next;
//    Q->data = Q->next->data;
//    Q->next = q->next;
//    free(q);
}
  • 求表长长度
ElemType ListLength(LinkList L) {
    int count = 0;
    LinkList p = L;
    if (p->next == nullptr)
        return 0;
    while (p->next != nullptr) {    //(p->next != nullptr指下一个结点, p != nullptr指当前结点)
        p = p->next;
        count++;
    }
    cout << "个数为:" << count << endl;
    return count;
}
  • 输出链表(针对有头结点的)
void List_show(LinkList &L) {
    if (L->next == nullptr)
        return;
    LinkList p = L;
    p = p->next;
//    do {
//        cout << p->data << " ";
//        p = p->next;
//    } while (L->next != nullptr);
    int m = ListLength(L);
    for (int i = 0; i < m; i++) {
        cout << p->data << " ";
        p = p->next;
    }
}

2. 双链表

typedef struct DNode {
    ElemType data;
    struct DNode *prior, *next;
} DNode, *DLinkList;
  • 双链表的插入操作(头插法)
DLinkList DListInsert(DLinkList &L, int i, ElemType e) {
    DLinkList p = DGetElem(L, i - 1);   //在p后面插入s
    DLinkList s;
    s = (DLinkList) malloc(sizeof(DNode));
    s->next = p->next;
    p->next->prior = s;
    s->prior = p;
    p->next = s;
    return L;
}
  • 双链表的删除操作(头插法)
DLinkList DListDelete(DLinkList &L, int i) {
    DLinkList p = DGetElem(L, i - 1);   //在p后面删除q
    DLinkList q = p->next;
    p->next = q->next;
    q->next->prior = p;
    free(q);
    return L;
}

3. 循环单/双链表

/*
void 判空() {
	LinkList M;
	DLinkList L;

M->next == M;		//循环单链表判空

L->next == L;
L->prior == L;		//循环双链表判空

}
*/

4. 静态链表,用数组实现链式的存储方式(适用于不借助指针)

typedef struct SDNode {
    ElemType data;
    int next;
} SLinkList[MaxSize];

6. 练习题

//设计一个递归算法,删除不带头结点的单链表L中所有值为x的结点
void test1_Delete_x(LinkList &L, ElemType x) {
//法一:交换前后值删除结点
    if (L->next == nullptr)
        return;
    if (L->data == x) {
        LinkList p = L->next;
        L->data = L->next->data;
        L->next = p->next;
        free(p);
        test1_Delete_x(L, x);
    } else {
        test1_Delete_x(L->next, x);     //这里不能先让 L=L->next,再把L代入test1_Delete_x,会导致所有结果都会消失,
    }
//法二:直接删除结点
//    LinkList p;
//    if (L->next == nullptr)
//        return;
//    if (L->data == x) {
//        p = L;
//        L = L->next;
//        free(p);
//        test1_Delete_x(L, x);
//    } else
//        test1_Delete_x(L->next, x);   //这里不能先让 L=L->next,再把L代入test1_Delete_x,会导致所有结果都会消失,
}

//在带头结点的单链表L中,删除所有值为x的结点,释放其空间
void test2_Delete_x(LinkList &L, ElemType x) {
//法一:由p从头扫描到尾,每一次都要连接前驱和后继
    if (L->next == nullptr)
        return;
    LinkList p = L->next;
    LinkList pre = L;   //pre作为p的前驱,起连接作用
//    int Len = ListLength(L);
//    for (int i = 0; i < Len; i++) {
    while (p != nullptr) {
        if (p->data == x) {
            LinkList q = p;
            p = p->next;
            pre->next = p;
            free(q);
        } else {
            pre = p;
            p = p->next;
        }
    }
//法二:采用尾插法建立单链表
//    LinkList p = L->next, r = L, q;
//    while (p != nullptr) {
//        if (p->data != x) {
//            r->next = p;
//            r = p;
//            p = p->next;
//        } else {
//            q = p;
//            p = p->next;
//            free(q);
//        }
//    }
//    r->next = nullptr;
}

//L为带头结点的单链表,实现反向输出每个结点的值
//本题为递归思想,也可以用逆置法,相当于尾插法
void test3_Reverse_output(LinkList L) {
    if (L->next != nullptr)
        test3_Reverse_output(L->next);
    cout << L->data << " ";
}

//带头结点的单链表L,删除其最小值的结点
LinkList test4_Delete_min(LinkList &L) {
    LinkList pre = L, p = pre->next, minpre = pre, minp = p;
    while (p != nullptr) {
        if (p->data < minp->data) {
            minp = p;
            minpre = pre;
        }
        pre = p;
        p = p->next;
    }
    minpre->next = minp->next;
    free(minp);
    return L;
}

//将一个带头结点的单链表就地逆置,即空间复杂度为O(1),即不能新建一个链表
LinkList test5_Reverse_Local(LinkList &L) {
//法一:先把头结点摘下来,然后用头插法的方法从第一个结点开始吧结点插在头结点的后面
//    LNode *p, *r;
//    p = L->next;
//    L->next = nullptr;        //把头结点摘下
//    while (p != nullptr) {
//        r = p->next;
//        p->next = L->next;
//        L->next = p;
//        p = r;
//    }
//    return L;
//法二:把各结点的指针反向指
    LNode *pre, *p = L->next, *r = p->next;
    p->next = nullptr;      //这个要作为尾结点
    while (r != nullptr) {
        pre = p;
        p = r;
        r = r->next;
        p->next = pre;
    }
    L->next = p;
    return L;
}

//使一个带头结点的单链表有序递增
void test6_increasing_order(LinkList &L) {
    LinkList p = L->next, pre;
    LinkList r = p->next;   //r始终要为p的后继结点
    p->next = nullptr;      //从原链表断开,和头结点构造一个有序的链表
    p = r;      //令p指针指回还未排序的原链表
    while (p != nullptr) {
        r = p->next;
        pre = L;    //pre始终要从头结点开始
        while (pre->next != nullptr && p->next->data < p->data)
            pre = pre->next;
        p->next = pre->next;
        pre->next = p;
        p = r;    //令p指针指回还未排序的原链表
    }
}

//带头结点的无序单链表L,删除所有值在(s, e)之间的元素,不包括s和e
void test7_Delete_s_e(LinkList &L, ElemType s, ElemType e) {
    LinkList p = L->next, pre = L;
    if (L->next == nullptr)
        return;
    while (p != nullptr) {
        if (p->data > s && p->data < e) {
            LinkList q = p;
            p = p->next;
            pre->next = p;
            free(q);
            //或者
//            pre->next = p->next;
//            free(p);
//            p = pre->next;
        } else {
            pre = p;
            p = p->next;
        }
    }
}

//给定两个单链表,找出两个链表的公共结点(公共结点指某一时刻两个链表的某个节点->next会指向同一个结点,
// 并且之后的结点都是公共的,直到尾结点,会形成一个 Y 型)
//暴力法:逐个检查,时间复杂度为O(n?)
//线性时间复杂度法O(len1+len2):先求出长度差,减去差后同步寻找
LinkList test8_find_common_LNode(LinkList L1, LinkList L2) {
    int len1 = ListLength(L1), len2 = ListLength(L2);
    LinkList longList, shortList;
    int dist;
    if (len1 > len2) {
        longList = L1->next;
        shortList = L2->next;
        dist = len1 - len2;
    } else {
        longList = L2->next;
        shortList = L1->next;
        dist = len2 - len1;
    }
    while (dist--)
        longList = longList->next;
    while (longList != nullptr) {
        if (longList == shortList)
            return longList;
        else {
            longList = longList->next;
            shortList = shortList->next;
        }
    }
    return nullptr;
}
  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

素履、

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值