链表的特点及应用(链表具有的特点)

链表是一种常用的数据结构,它具有许多特点,比如动态性、添加/删除节点方便、灵活性强等特点。在本篇文章中,我们将从不同的角度来详细阐述链表的特点及其应用。

一、链表的定义

链表是一种线性数据结构,它由多个节点组成,每个节点包含两个部分,一个是数据域,用来存储数据,另一个是指针域,用来指向下一个节点的地址。链表的特点是不同于数组和其他数据结构的动态性,因为链表可以按需分配空间并且在运行时添加或删除元素,所以链表在很多场景下都非常适用。

二、链表的种类

链表主要有单向链表、双向链表和循环链表三种,它们在结构上略有不同,但都具备链表的基本特点。

1. 单向链表

单向链表是指每个节点只有指向下一节点的指针,不能回溯到前面的节点。


class ListNode {
public:
    int val;
    ListNode *next;
    ListNode(int x) : val(x), next(NULL) {}
};

2. 双向链表

双向链表是指每个节点都有指向前面和后面节点的指针,可以在两个方向上遍历。


class ListNode {
public:
    int val;
    ListNode *prev;
    ListNode *next;
    ListNode(int x) : val(x), prev(NULL), next(NULL) {}
};

3. 循环链表

循环链表是指最后一个节点的指针指向第一个节点,形成了一个环状结构。


class ListNode {
public:
    int val;
    ListNode *next;
    ListNode(int x) : val(x), next(NULL) {}
};

三、链表的操作

1. 遍历链表

遍历链表是指依次访问链表中的所有节点,可以使用while循环或者递归方式实现。


void traverseList(ListNode* head) {
    ListNode* p = head;
    while (p != NULL) {
        // 进行一些操作,比如输出节点值
        cout <val <next;
    }
}

2. 在链表中添加节点

添加节点是指在链表中插入一个新的节点,通常采用头插法和尾插法两种方式实现。

(1)头插法

在链表的头部插入一个新的节点。


ListNode* addAtHead(ListNode* head, int val) {
    ListNode* newNode = new ListNode(val);
    newNode->next = head;
    head = newNode;
    return head;
}

(2)尾插法

在链表的尾部插入一个新的节点。


ListNode* addAtTail(ListNode* head, int val) {
    ListNode* newNode = new ListNode(val);
    if (head == NULL) {
        head = newNode;
        return head;
    }
    ListNode* p = head;
    while (p->next != NULL) {
        p = p->next;
    }
    p->next = newNode;
    return head;
}

3. 在链表中删除节点

删除节点是指删除链表中的一个节点,通常按节点值和节点位置来删除。

(1)按节点值删除

在链表中查找节点值等于给定值,然后删除该节点。


ListNode* deleteNode(ListNode* head, int val) {
    if (head == NULL) {
        return NULL;
    }
    ListNode* p = head;
    if (head->val == val) {
        head = head->next;
        return head;
    }
    while (p->next != NULL && p->next->val != val) {
        p = p->next;
    }
    if (p->next != NULL) {
        p->next = p->next->next;
    }
    return head;
}

(2)按节点位置删除

在链表中根据节点位置来删除节点,通常需要保存要删除节点的前一个节点。


ListNode* deleteNode(ListNode* head, int pos) {
    if (head == NULL) {
        return NULL;
    }
    ListNode* p = head;
    if (pos == 0) {
        head = head->next;
        return head;
    }
    for (int i = 0; i next != NULL; i++) {
        p = p->next;
    }
    if (p->next != NULL) {
        p->next = p->next->next;
    }
    return head;
}

四、链表的应用

链表广泛应用于各种数据结构和算法中,比如队列、栈、哈希表等。下面是链表应用的一些实际例子。

1. LRU缓存淘汰算法

LRU缓存淘汰算法是一种利用链表实现的缓存淘汰策略,当缓存满时,淘汰最近最少使用的节点。


class LRUCache {
private:
    struct Node {
        int key, val;
        Node *next, *prev;
        Node(int _key, int _val) : key(_key), val(_val), next(NULL), prev(NULL) {}
    };
    int _capacity;
    Node *head, *tail;

public:
    LRUCache(int capacity) : _capacity(capacity) {
        head = new Node(0, 0);
        tail = new Node(0, 0);
        head->next = tail;
        tail->prev = head;
    }

    int get(int key) {
        if (cache.find(key) == cache.end()) {
            return -1;
        }
        Node *node = cache[key];
        deleteNode(node);
        addToHead(node);
        return node->val;
    }

    void put(int key, int value) {
        if (cache.find(key) != cache.end()) {
            Node *node = cache[key];
            node->val = value;
            deleteNode(node);
            addToHead(node);
        } else {
            if (cache.size() == _capacity) {
                Node *tailNode = tail->prev;
                cache.erase(tailNode->key);
                deleteNode(tailNode);
            }
            Node *node = new Node(key, value);
            cache[key] = node;
            addToHead(node);
        }
    }

    void addToHead(Node *node) {
        node->next = head->next;
        head->next->prev = node;
        head->next = node;
        node->prev = head;
    }

    void deleteNode(Node *node) {
        node->prev->next = node->next;
        node->next->prev = node->prev;
    }
};

2. 链表求解环形结构问题

链表求解环形结构问题是指判断链表中是否存在环状结构,并可以找出环状结构的入口节点。


ListNode* detectCycle(ListNode* head) {
    if (head == NULL || head->next == NULL) {
        return NULL;
    }
    ListNode* fast = head, *slow = head;
    while (fast != NULL && fast->next != NULL) {
        fast = fast->next->next;
        slow = slow->next;
        if (fast == slow) {
            ListNode* p = head;
            while (p != slow) {
                p = p->next;
                slow = slow->next;
            }
            return p;
        }
    }
    return NULL;
}

3. 链表操作综合案例

下面是一个基于链表操作的综合案例,实现了链表的逆转、排序、合并、删除重复元素等操作。


class Solution {
public:
    ListNode* reverseList(ListNode* head) {
        if (head == NULL || head->next == NULL) {
            return head;
        }
        ListNode* newHead = reverseList(head->next);
        head->next->next = head;
        head->next = NULL;
        return newHead;
    }

    ListNode* sortList(ListNode* head) {
        if (head == NULL || head->next == NULL) {
            return head;
        }
        ListNode* slow = head, *fast = head->next;
        while (fast != NULL && fast->next != NULL) {
            slow = slow->next;
            fast = fast->next->next;
        }
        ListNode* mid = slow->next;
        slow->next = NULL;
        return merge(sortList(head), sortList(mid));
    }

    ListNode* merge(ListNode* l1, ListNode* l2) {
        ListNode dummy(0), *p = &dummy;
        while (l1 != NULL && l2 != NULL) {
            if (l1->val val) {
                p->next = l1;
                l1 = l1->next;
            } else {
                p->next = l2;
                l2 = l2->next;
            }
            p = p->next;
        }
        if (l1 != NULL) {
            p->next = l1;
        }
        if (l2 != NULL) {
            p->next = l2;
        }
        return dummy.next;
    }

    ListNode* deleteDuplicates(ListNode* head) {
        if (head == NULL) {
            return NULL;
        }
        ListNode* p = head;
        while (p->next != NULL) {
            if (p->val == p->next->val) {
                p->next = p->next->next;
            } else {
                p = p->next;
            }
        }
        return head;
    }
};

总结

链表是一种非常常用的数据结构,它具有许多特点,比如动态性、添加/删除节点方便、灵活性强等特点,而且在各种算法和数据结构的实现中广泛应用。在学习的过程中,我们需要熟练掌握链表的基本操作,理解不同种类的链表,并在实践中掌握链表的常见应用,提高算法和编程能力。

Published by

风君子

独自遨游何稽首 揭天掀地慰生平