数据结构 单向链表

单向链表

定义

  • 存储元素的同时记录下一个结点的地址

  • 链表的内存地址可以不连续

  • 对比顺序表需要额外的空间存储next指针

ElemType elem;  // 结点存储的元素
struct SNode *next; // 下一个结点的地址

优缺点

  • 优点

    • 内存不需要连续,可以是零散的内存空间

    • 在头部插入和删除的效率很高 O(1)

    • 在指定位置后插入和删除的效率很高 O(1)

    • 不需要提前分配好空间,有多少元素就需要的少空间,没有内存闲置

  • 缺点

    • 需要额外的空间来存储指针域,内存利用率不高

    • 不支持随机访问,查找指定位置元素的效率为o(n)

    • 在末尾插入和删除的效率差 O(n)

    • 链表不能进行二分查找

  • 什么时候用单向链表

    • 数据量不确定

    • 数据变化较大(插入和删除)

    • 增加和删除元素经常在头部进行时

单向链表实现

结构体定义

有元素和指针两个部分

typedef int ElemType;

// 单向链表结点
struct SNode {
    ElemType elem;  // 结点存储的元素
    struct SNode *next; // 下一个结点的地址
};

// 结点大小宏
#define SNODESIZE sizeof(struct SNode)

// 单向链表类型
typedef struct SNode* SLinkedList;

#define SUCCESS 0 // 成功返回值
#define FAILURE -1 // 失败返回值

基础函数声明

// 创建一个单向链表
SLinkedList create_slinkedlist(void);

// 单向链表是否为空
bool empty_slinkedlist(SLinkedList list);

// 元素个数
size_t size_slinkedlist(SLinkedList list);

// 在指定位置插入一个元素
int insert_slinkedlist(SLinkedList list, size_t pos, ElemType elem);

// 在头部插入元素
int push_front_slinkedlist(SLinkedList list, ElemType elem);

// 删除指定位置元素
int remove_slinkedlist(SLinkedList list, size_t pos, ElemType *pElem);

// 删除第一个元素
int pop_front_slinkedlist(SLinkedList list, ElemType *pElem);

// 删除第n个为elem的元素   n == 0 表示全删
int delete_slinkedlist(SLinkedList list, ElemType elem, size_t n);

// 根据条件删除第n个元素    需要传入condition函数
int delete_condition_slinkedlist(SLinkedList list, bool (condition)(ElemType), size_t n);

// 查找位置为pos的元素
ElemType* elem_of_slinkedlist(SLinkedList list, size_t pos);

// 查找第n个为elem的元素
int find_slinkedlist(SLinkedList list, ElemType elem, size_t n);

// 根据条件删除第n个元素
int find_condition_slinkedlist(SLinkedList list, bool (*condition)(ElemType), size_t n);

// 统计为elem元素的个数
size_t count_slinkedlist(SLinkedList list, ElemType elem);

// 统计满足条件元素的个数
size_t coount_condition(SLinkedList list, bool (*condition)(ElemType));

// 更新pos位置的元素为newElem
int update_slinkedlist(SLinkedList list, size_t pos, ElemType newElem);

// 清空链表
void clear_slinkedlist(SLinkedList list);

// 销毁链表
void destroy_slinkedlist(SLinkedList list);

// 迭代
void foreach_slinkedlist(SLinkedList list, void (*foreach)(ElemType*));





// 静态函数

// 获取第pos个位置的结点
static struct SNode* get_node_slinkedlist(SLinkedList list, size_t pos);

// 用elem和next创建一个单向链表的结点
static struct SNode* create_snode(ElemType elem, struct SNode *next);

// 在指定结点node后面插入一个元素
static int insert_after(struct SNode *node, ElemType elem);

// 删除指定结点后面一个结点
static int delete_after(struct SNode *prevNode, ElemType *pElem);

静态函数实现

获取第pos个位置的结点 get_node_slinkedlist

获取第pos个位置的结点 pos为0表示获取头结点

如果pos位置不存在则返回NULL

static struct SNode* get_node_slinkedlist(SLinkedList list, size_t pos) {
    struct SNode *node = list;
    for (size_t i = 0; i < pos && node != NULL; ++i) {
        node = node->next;
    }
    return node;
}
用elem和next创建一个单向链表的结点 create_snode
static struct SNode* create_snode(ElemType elem, struct SNode *next) {
    struct SNode *node = (struct SNode*)malloc(SNODESIZE);
    if (node != NULL) {
        node->elem = elem;
        node->next = next;
    }
    return node;
}
在指定结点node后面插入一个元素 insert_after

node的next指针指向新建的结点

static int insert_after(struct SNode *node, ElemType elem) {
    if (node == NULL) {
        return FAILURE;
    }
    struct SNode *insNode = create_snode(elem, node->next);
    if (insNode == NULL) {
        return FAILURE;
    }
    node->next = insNode;
    return SUCCESS;
}
删除指定结点后面一个结点 delete_after

先判断下一个结点是否存在

主要prev->next = prev->next->next

注意要释放删除的结点,否则会造成内存泄漏!

static int delete_after(struct SNode *prevNode, ElemType *pElem) {
    // 前一个结点不存在 或者 要删除的结点不存在
    if (prevNode == NULL || prevNode->next == NULL) {
        return FAILURE;
    }
    // 记录要删除的结点
    struct SNode *delNode = prevNode->next;
    // 看调用者是否要保存删除的元素
    if (pElem != NULL) {
        *pElem = delNode->elem;
    }
    // 把delNode从单向链表中删除
    prevNode->next = delNode->next;
    // 释放结点的内存
    free(delNode);
    return SUCCESS;
}

基础函数实现

创建一个单向链表 create_slinkedlist

创建一个头结点,不用来存储数据

SLinkedList create_slinkedlist(void) {

    SLinkedList list = (SLinkedList)malloc(sizeof(SNODESIZE));
    if (list != NULL) {
        list->next = NULL; // 单向链表为空 即第一个结点为NULL(不存在)
    }
    return list;
}
单向链表是否为空 empty_slinkedlist

如果第一个结点都不存在则为空

bool empty_slinkedlist(SLinkedList list) {
    assert(list != NULL);
    return list->next == NULL;
}
元素个数 size_t size_slinkedlist

遍历链表统计元素个数

size_t size_slinkedlist(SLinkedList list) {
    assert(list != NULL);
    size_t cnt = 0;
    // while (list = list->next) {
    //     ++cnt;
    // }
    struct SNode *node = list->next;
    for (; node != NULL; node = node ->next, ++cnt);
    return cnt;
}
在指定位置插入一个元素 insert_slinkedlist

时间复杂度O(n)

int insert_slinkedlist(SLinkedList list, size_t pos, ElemType elem) {
    assert(list != NULL);
    if (pos == 0) {
        return FAILURE;
    }
    // 在pos位置插入,获取第pos-1个结点
    struct SNode *prev = get_node_slinkedlist(list, pos - 1);
    return insert_after(prev, elem); //调用静态函数
    
    /*
    if (prev == NULL) { // 前一个位置为空,则插入失败
        return FAILURE;
    }
    // 创建结点
    struct SNode *insNode = create_slinkedlist(elem, prev->next);
    if (insNode == NULL) {
        return FAILURE;
    }
    //前结点的next指针指向插入的结点
    prev->next = insNode; 
    return SUCCESS;
    */
}
在头部插入元素 push_front_slinkedlist

时间复杂度O(1)

int push_front_slinkedlist(SLinkedList list, ElemType elem) {
    assert(list != NULL);
    // 在单向链表头部插入 所以插入的结点的作为第一个结点
    // 原来的第一个结点就作为插入结点的next
    return insert_after(list, elem);
    /*
    static SNode *first = create_snode(elem, list->next);
    if (first == NULL) {
        return FAILURE;
    }
    list->next = first;
    return SUCCESS;
    */
}
删除指定位置元素 remove_slinkedlist

时间复杂度O(n)

int remove_slinkedlist(SLinkedList list, size_t pos, ElemType *pElem) {
    assert(list != NULL);
    if (pos == 0) {
        return FAILURE;
    }
    struct SNode *prev = get_node_slinkedlist(list, pos - 1);
    /*
    if (prev == NULL || prev->next == NULL) {
        return FAILURE;
    }
    */
    return delete_after(prev, pElem);
}
删除第一个元素 pop_front_slinkedlist

时间复杂度O(1)

int pop_front_slinkedlist(SLinkedList list, ElemType *pElem) {
    assert(list != NULL);
    return delete_after(list, pElem);
}
删除第n个为elem的元素 n == 0 表示全删 delete_slinkedlist

时间复杂度O(n)

int delete_slinkedlist(SLinkedList list, ElemType elem, size_t n) {
    assert(list != NULL);
    size_t cnt = 0;
    struct SNode *prev = list;
    while (prev->next != NULL) {
        if (prev->next->elem == elem) { // 等于要删除的结点
            if (n == 0) {
                delete_after(prev, NULL);
                ++cnt;
            } else {
                if (--n == 0) {
                    delete_after(prev, NULL);
                    return 1;
                } else {
                    prev = prev->next;
                }
            }
        } else {
            prev = prev->next;  // 删除元素后无需移动到下一个地址   删除后下一个元素补上来了
        }
    }
    return cnt;
}
根据条件删除第n个元素 需要传入condition函数 delete_condition_slinkedlist

自定义回调函数condition 时间复杂度O(n)

int delete_condition_slinkedlist(SLinkedList list, bool (condition)(ElemType), size_t n) {
    assert(list != NULL && condition != NULL);
    size_t cnt = 0;
    struct SNode *prev = list;
    while (prev->next != NULL) {
        if (condition(prev->next->elem)) {
            if (n == 0) {
                delete_after(prev, NULL);
                ++cnt;
            } else {
                if (--n == 0) {
                    delete_after(prev, NULL);
                    return 1;
                } else {
                    prev = prev->next;
                }
            }
        } else {
            prev = prev->next;
        }
    }
    return cnt;
}
查找位置为pos的元素 elem_of_slinkedlist

时间复杂度O(n)

ElemType* elem_of_slinkedlist(SLinkedList list, size_t pos) {
    assert(list != NULL);
    if (pos == 0) {
        return NULL;
    }
    struct SNode *node = get_node_slinkedlist(list, pos);
    if (node == NULL) {
        return NULL;
    }
    return &node->elem;
}
查找第n个为elem的元素 find_slinkedlist

时间复杂度O(n)

int find_slinkedlist(SLinkedList list, ElemType elem, size_t n) {
    assert(list != NULL);
    if (n == 0) {
        return 0; // 表示没找到
    }
    size_t pos = 0;
    struct SNode *node = list->next;
    for (; node != NULL; node = node->next) {
        ++pos;
        if (node->elem == elem) {
            if (--n == 0) {
                return pos;
            }
        }
    }
    return 0;
}
根据条件删除第n个元素 find_condition_slinkedlist

时间复杂度O(n)

int find_condition_slinkedlist(SLinkedList list, bool (*condition)(ElemType), size_t n) {
    assert(list != NULL && condition != NULL);
    if (n == 0) {
        return 0;
    }
    size_t pos = 0;
    struct SNode *node = list->next;
    for (; node != NULL; node = node->next) {
        ++pos;
        if (condition(node->elem)) {
            if (--n == 0) {
                return pos;
            }
        }
    }
    return 0;
}
统计为elem元素的个数count_slinkedlist

时间复杂度O(n)

size_t count_slinkedlist(SLinkedList list, ElemType elem) {
    assert(list != NULL);
    size_t cnt = 0;
    struct SNode *node = list->next;
    for (; node != NULL; node = node->next) {
        if (node->elem == elem) {  // 等于要统计的元素
            ++cnt;
        }
    }
    return cnt;
}
统计满足条件元素的个数 coount_condition

时间复杂度O(n)

size_t coount_condition(SLinkedList list, bool (*condition)(ElemType)) {
    assert(list != NULL && condition != NULL);
    size_t cnt = 0;
    struct SNode *node = list->next;
    for (; node != NULL; node = node->next) {
        if (condition(node->elem)) {  // 满足条件的元素
            ++cnt;
        }
    }
    return cnt;
}
更新pos位置的元素 update_slinkedlist

时间复杂度O(n)

int update_slinkedlist(SLinkedList list, size_t pos, ElemType newElem) {
    assert(list != NULL);
    if (pos == 0) {
        return FAILURE;
    }
    struct SNode *node = get_node_slinkedlist(list, pos);
    if (node == NULL) {
        return FAILURE;
    }
    node->elem = newElem;
    return SUCCESS;
}
清空链表 clear_slinkedlist

时间复杂度O(n)

每个结点都要释放,否则会造成内存泄漏

void clear_slinkedlist(SLinkedList list) {
    assert(list != NULL);
    struct SNode *node, *next;
    for (node = list->next; node != NULL; node = next) {
        next = node->next; // 先记录下一个结点
        free(node);
    }
    list->next = NULL;
}
销毁链表 destroy_slinkedlist

先清空链表,再释放头结点

void destroy_slinkedlist(SLinkedList list) {
    assert(list != NULL);
    clear_slinkedlist(list); // 清空所有元素
    free(list); // 释放头结点
}
迭代 foreach_slinkedlist

需要传入自定义迭代函数foreach

可以用来实现打印元素的操作

void foreach_slinkedlist(SLinkedList list, void (*foreach)(ElemType*)) {
    assert(list != NULL);
    struct SNode *node = list->next;
    for (; node != NULL; node = node->next) {
        foreach(&node->elem);
    }
}

单向链表常见题型

函数声明

// 单向链表逆序
void reverse_slinkedlist(SLinkedList list);

// 按count个元素进行逆序
void reverse_count_slinkedlist(SLinkedList list, size_t count);

// 合并两个升序的单向链表l1和l2为l3
void merge_slinkedlist(SLinkedList l1, SLinkedList l2, SLinkedList l3);

// 求两个单向链表的尾链长度
size_t common_tail_slinkedlist(SLinkedList l1, SLinkedList l2);

// 判断单链表是否有环
size_t circle_slinkedlist(SLinkedList list);

函数实现

单向链表逆序 reverse_slinkedlist
void reverse_slinkedlist(SLinkedList list) {
    assert(list != NULL);
    if (list->next == NULL || list->next->next == NULL) {
        return; // NULL
    }
    struct SNode *prev = NULL; // 上一个结点
    struct SNode *curr = list->next; // 当前结点
    struct SNode *next = NULL; // 下一个结点
    while (curr != NULL) {
        next = curr->next; // 先记录下一个结点
        curr->next = prev; // 让指向下一个结点的指针指向上一个结点
        prev = curr; // 向后移动
        curr = next; // 向后移动
    }
    list->next = prev; // 让头结点指向逆序后的第一个结点
    // return list;
}
按count个元素进行逆序 reverse_count_slinkedlist
void reverse_count_slinkedlist(SLinkedList list, size_t count) {
    assert(list != NULL && count != 1);
    struct SNode *last = list; // 已经逆序的最后一个结点
    struct SNode *prev = NULL;
    struct SNode *curr = list->next;
    struct SNode *next = NULL;
    struct SNode *first = NULL; // 当前count个逆序结点的第一个
    while (curr != NULL) {
        first = curr;
        prev = NULL; // 要逆序的count个结点的第一个next指向prev NULL
        for (size_t i = 0; i < count && curr != NULL; ++i) { // 每count个逆序 或最后不够count个
            next = curr->next;
            curr->next = prev;
            prev = curr;
            curr = next;
        }
        last->next = prev;  // 已经逆序的最后一个结点的next指向 刚刚逆序的count个结点的第一个结点
        last = first;   // 现在这count个的第一个结点变成逆序好的最后一个结点
    }
    // return list;
}
合并两个升序的单向链表l1和l2为l3 merge_slinkedlist

归并排序

void merge_slinkedlist(SLinkedList l1, SLinkedList l2, SLinkedList l3) {
    assert(l1 != NULL && l2 != NULL && l3 != NULL);
    struct SNode *node1 = l1->next;
    struct SNode *node2 = l2->next;
    struct SNode *curr = l3;
    while (node1 != NULL && node2 != NULL) {
        if (node1->elem < node2->elem) {  // curr的next指向 较小的元素的结点
            curr->next = node1;
            node1 = node1->next;
        } else {
            curr->next = node2;
            node2 = node2->next;
        }
        curr = curr->next; // 当前curr向后移动
    }
    curr->next = node1 != NULL ? node1 : node2; // 有一个不为空  把整条链接上
    l1->next = NULL;
    l2->next = NULL;
}
求两个单向链表的尾链长度 common_tail_slinkedlist
size_t common_tail_slinkedlist(SLinkedList l1, SLinkedList l2) {
    assert(l1 != NULL && l2 != NULL);
    size_t len1 = size_slinkedlist(l1); // 计算单向链表l1的长度
    size_t len2 = size_slinkedlist(l2); // 计算单向链表l2的长度
    struct SNode *node1 = l1->next;
    struct SNode *node2 = l2->next;
    if (len1 > len2) { // 先把较长的链移动到 与较短的的链一样的长度
        while (len1 != len2) {
            --len1;
            node1 = node1->next;
        }
    } else {
        while (len1 != len2) {
            --len2;
            node2 = node2->next;
        }
    }
    while (node1 != NULL && node2 != NULL && node1 != node2) { // 一起移动到相同的结点为止
        --len1;
        node1 = node1->next;
        node2 = node2->next;
    }
    return len1; // 移动后剩下的长度就时共同尾链的长度
}
判断单链表是否有环 circle_slinkedlist
size_t circle_slinkedlist(SLinkedList list) {
    assert(list != NULL);
    if (list->next == NULL || list->next->next == NULL) {
        return 0;
    }
    struct SNode *quick = list->next; // 快指针 每次移动2个结点
    struct SNode *slow = list->next; // 慢指针 每次移动1个结点
    while (quick != NULL && quick->next != NULL) { // 如果有环则快指针会追上慢指针
        quick = quick->next->next;
        slow = slow->next;
        if (quick == slow) {
            size_t n = 0;
            do { // 在相遇点在跑一圈就是环的长度
                ++n;
                quick = quick->next;
            } while (quick != slow);
            return n;
        }
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值