链表专题

原文链接:https://www.acwing.com/blog/content/432/

单向链表

链表是一种线性表,但是并不会按线性的顺序存储数据,而是在每一个节点里存到下一个节点的指针。由于不必须按顺序存储,链表在插入的时候可以达到O(1)的复杂度,比另一种线性表顺序表快得多,但是查找一个节点或者访问特定编号的节点则需要O(n)的时间,而顺序表相应的时间复杂度分别是O(logn)和O(1)。
使用链表结构可以克服数组需要先知道数据大小的缺点,链表结构可以充分利用内存空间。但是链表失去了数组随机读取的优点,同时链表由于增加了结点的指针域,空间开销比较大。

静态链表

int head, e[N], ne[N], idx;

// 初始化
void init()
{
    head = -1;
    idx = 0;
}

// 在链表头插入一个数a
void insert(int a)
{
    e[idx] = a, ne[idx] = head, head = idx ++ ;
}

//向表中k位置插图x
void add(int k,int x){
    n[idx]=x;
    ne[idx]=ne[k];
    ne[k]=idx++;
}

// 将k删除,需要保证头结点存在
void remove(int k)
{
    ne[k]=ne[ne[k]];

}

动态链表

下面展示一些 内联代码片

typedef int  ElemType;
struct LNode {
    ElemType data;
    struct LNode *next;
} LNode, *LinkList;
//初始化

void init(LinkList *L) {
    *L = (LinkList)malloc(sizeof(struct LNode));
    if (!*L) //存储分配失败
        exit(-1);
    (*L)->next = NULL; 
}
//获取元素个数(长度)
int Length(LinkList L) {
    int i = 0;
    LinkList p = L->next; // p指向第一个结点
    while (p) { // 没到表尾
        i++;
        p = p->next;
    }
    return i;
}
//查找
int Find(LinkList L, int i, ElemType *e) {

    int j = 1; // j为检索
    LinkList p = L->next; // p指向第一个结点
    while (p && j < i) { // 顺指针向后查找,直到p指向第i个元素或p为空
        p = p->next;
        j++;
    }
    if (!p || j > i) // 第i个元素不存在
        return 0;
    *e = p->data; // 取第i个元素
    return e;
}
bool Insert(LinkList L, int i, ElemType e) {

    // 表L中第i个位置之前插入元素e
    int j = 0;
    LinkList p = L, s;
    while (p && j < i - 1) { // 寻找第i-1个结点
        p = p->next;
        j++;
    }
    if (!p || j > i - 1) // i小于1或者大于表长
        return false;
    s = (LinkList)malloc(sizeof(struct LNode)); // 动态地分配内存空间,生成新结点
    s->data = e; // 插入L中
    s->next = p->next;
    p->next = s;
    return true;
}

bool Delete(LinkList L, int i, ElemType *e) {
    int j = 0;
    LinkList p = L, q;
    while (p->next && j < i - 1) { // 寻找第i个结点,并令p指向其前驱结点
        p = p->next;
        j++;
    }
    if (!p->next || j > i - 1) // 删除位置不合理
        return flase;
    q = p->next; // 删除并释放结点
    p->next = q->next;
    *e = q->data;
    free(q);
   return true;
}

双向链表

有了单向链表的基础,我们就可以很好的去理解双向链表,下面我们来看一下:

它的每个数据结点中都有两个指针,分别指向直接后继和直接前驱。所以,从双向链表中的任意一个结点开始,都可以很方便地访问它的前驱结点和后继结点。他弥补了单向链表的缺点,一般我们都构造双向循环链表。

动态模板

struct DuLNode {
    ElemType data;
    struct DuLNode *prior, *next;
} DuLNode, *DuLinkList;


//初始化
void init(DuLinkList *L) {
    *L = (DuLinkList)malloc(sizeof(DuLNode));
        (*L)->next = (*L)->prior = *L;

}

//判断是否为空
bool  is_Empty(DuLinkList L) {

    if (L->next == L && L->prior == L)
        return true;
    else
        return false;
}
//获取长度
intLength(DuLinkList L) {
    int i = 0;
    DuLinkList p = L->next; // p指向第一个结点
    while (p != L) { // p没到表头
        i++;
        p = p->next;
    }
    return i;
}

//查找
int Find(DuLinkList L, int i, ElemType *e) {
    int j = 1; // j为索引值
    DuLinkList p = L->next; // p指向第一个结点
    while (p != L && j < i) { // 顺指针向后查找,直到p指向第i个元素或p指向头结点
        p = p->next;
        j++;
    }
    if (p == L || j > i) // 第i个元素不存在
        return 0;
    *e = p->data; // 取第i个元素
    return e;
}
// L中第i个位置之前插入元素e,i的合法值为1<=i<=l.length+1,否则无法在第(length+1)个结点之前插入元素
boll Insert(DuLinkList L, int i, ElemType e) {

    DuLinkList p, s;
    if (i < 1 || i > ListLength(L) + 1) // i值不合法
        return ERROR;
    p = GetElemP(L, i - 1); // 在L中确定第i个元素前驱的位置指针p
    if (!p) // p=NULL,即第i个元素的前驱不存在(设头结点为第1个元素的前驱)
        return false;
    s = (DuLinkList)malloc(sizeof(DuLNode));
    if (!s)
        return OVERFLOW;
    s->data = e;
    s->prior = p; // 在第i-1个元素之后插入
    s->next = p->next;
    p->next->prior = s;
    p->next = s;
    return true;
}
// 表L的第i个元素,i的合法值为1<=i<=length
bool Delete(DuLinkList L, int i, ElemType *e) {

    DuLinkList p;
    if (i < 1) // i值不合法
        return true;
    p = GetElemP(L, i); // 在L中确定第i个元素的位置指针p
    if (!p)   // p = NULL,即第i个元素不存在
        return ERROR;
    *e = p->data;
    p->prior->next = p->next; // 此处并没有考虑链表头,链表尾
    p->next->prior = p->prior;
    free(p);//释放空间
    return false;
}

静态模板

// e[]表示节点的值,l[]表示节点的左指针,r[]表示节点的右指针,idx表示当前用到了哪个节点
int e[N], l[N], r[N], idx;

// 初始化
void init()
{
    //0是左端点,1是右端点
    r[0] = 1, l[1] = 0;
    idx = 2;
}

// 在节点a的右边插入一个数x
void insert(int a, int x)
{
    e[idx] = x;
    l[idx] = a, r[idx] = r[a];
    l[r[a]] = idx, r[a] = idx ++ ;
}

// 删除节点a
void remove(int a)
{
    l[r[a]] = l[a];
    r[l[a]] = r[a];
}

``

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值