链表(Linked list)

相比数组,链表是一种稍微复杂一点的数据结构。数组要求一块连续内存空间来存储,对内存要求比较高。而链表通过指针将一组零散的内存块串联起来使用,不需要内存地址连续。
链表的结构五花八门,最常见的三种链表结构是单链表,双向链表和循环链表。
刚刚讲到链表通过指针将一组零散的内存块串联在一起。其中我们把内存块成为链表的**“结点”。为了将所有的节点串起来,每个链表的节点除了存储数据之外,还需要记录链上的下一个节点的地址,我们把这个记录下个结点地址的指针叫做后继指针next**。
在这里插入图片描述
单链表中有两个结点是比较特殊的,第一个结点头结点跟最后一个结点尾结点。头结点用来记录链表的基地址,有了它就可以遍历得到整条链表。尾结点的指针指向一个空地址NULL,表示这是链表上最后一个结点。

与数组一样链表也支持数据的查找、插入和删除操作。针对链表的插入和删除操作,我们只需要考虑相邻结点的指针改变,所以对应时间复杂度是O(1)。链表随机访问的性能没有数组好,需要 O(n) 的时间复杂度。
在这里插入图片描述
循环链表跟单链表唯一的却别就在尾结点,循环链表的尾结点指针指向链表的头结点。
循环链表的优点是从链尾到链头比较方便。当要处理的数据具有环形结构特点时,就特别适合采用循环链表。
在这里插入图片描述
在实际的软件开发中,更加常用的链表结构:双向链表。双向链表需要额外的两个空间来存储后继结点和前驱结点的地址。所以,如果存储同样多的数据,双向链表要比单链表占用更多的内存空间。虽然两个指针比较浪费存储空间,但可以支持双向遍历。
双向链表可以支持 O(1) 时间复杂度的情况下找到前驱结点,正是这样的特点,也使双向链表在某些情况下的插入、删除等操作都要比单链表简单、高效。
在这里插入图片描述

class Node(object):
    """链表结构的Node节点"""

    def __init__(self, data, next_node=None):
        """Node节点的初始化方法.
        参数:
            data:存储的数据
            next:下一个Node节点的引用地址
        """
        self.__data = data
        self.__next = next_node

    @property
    def data(self):
        """Node节点存储数据的获取.
        返回:
            当前Node节点存储的数据
        """
        return self.__data

    @data.setter
    def data(self, data):
        """Node节点存储数据的设置方法.
        参数:
            data:新的存储数据
        """
        self.__data = data

    @property
    def next_node(self):
        """获取Node节点的next指针值.
        返回:
            next指针数据
        """
        return self.__next

    @next_node.setter
    def next_node(self, next_node):
        """Node节点next指针的修改方法.
        参数:
            next:新的下一个Node节点的引用
        """
        self.__next = next_node


class SinglyLinkedList(object):
    """单向链表"""

    def __init__(self):
        """单向列表的初始化方法."""
        self.__head = None

    def find_by_value(self, value):
        """按照数据值在单向列表中查找.
        参数:
            value:查找的数据
        返回:
            Node
        """
        node = self.__head
        while (node is not None) and (node.data != value):
            node = node.next_node
        return node

    def find_by_index(self, index):
        """按照索引值在列表中查找.
        参数:
            index:索引值
        返回:
            Node
        """
        node = self.__head
        pos = 0
        while (node is not None) and (pos != index):
            node = node.next_node
            pos += 1
        return node

    def insert_to_head(self, value):
        """在链表的头部插入一个存储value数值的Node节点.
        参数:
            value:将要存储的数据
        """
        node = Node(value)
        node.next_node = self.__head
        self.__head = node

    def insert_after(self, node, value):
        """在链表的某个指定Node节点之后插入一个存储value数据的Node节点.
        参数:
            node:指定的一个Node节点
            value:将要存储在新Node节点中的数据
        """
        if node is None:  # 如果指定在一个空节点之后插入数据节点,则什么都不做
            return

        new_node = Node(value)
        new_node.next_node = node.next
        node.next = new_node

    def insert_before(self, node, value):
        """在链表的某个指定Node节点之前插入一个存储value数据的Node节点.
        参数:
            node:指定的一个Node节点
            value:将要存储在新的Node节点中的数据
        """
        if (node is None) or (self.__head is None):  # 如果指定在一个空节点之前或者空链表之前插入数据节点,则什么都不做
            return

        if node == self.__head:  # 如果是在链表头之前插入数据节点,则直接插入
            self.insert_to_head(value)
            return

        new_node = Node(value)
        pro = self.__head
        not_found = False  # 如果在整个链表中都没有找到指定插入的Node节点,则该标记量设置为True
        while pro.next_node != node:  # 寻找指定Node之前的一个Node
            if pro.next_node is None:  # 如果已经到了链表的最后一个节点,则表明该链表中没有找到指定插入的Node节点
                not_found = True
                break
            else:
                pro = pro.next_node
        if not not_found:
            pro.next_node = new_node
            new_node.next_node = node

    def delete_by_node(self, node):
        """在链表中删除指定Node的节点.
        参数:
            node:指定的Node节点
        """
        if self.__head is None:  # 如果链表是空的,则什么都不做
            return

        if node == self.__head:  # 如果指定删除的Node节点是链表的头节点
            self.__head = node.next_node
            return

        pro = self.__head
        not_found = False  # 如果在整个链表中都没有找到指定删除的Node节点,则该标记量设置为True
        while pro.next_node != node:
            if pro.next_node is None:  # 如果已经到链表的最后一个节点,则表明该链表中没有找到指定删除的Node节点
                not_found = True
                break
            else:
                pro = pro.next_node
        if not not_found:
            pro.next_node = node.next_node

    def delete_by_value(self, value):
        """在链表中删除指定存储数据的Node节点.
        参数:
            value:指定的存储数据
        """
        if self.__head is None:  # 如果链表是空的,则什么都不做
            return

        if self.__head.data == value:  # 如果链表的头Node节点就是指定删除的Node节点
            self.__head = self.__head.next_node

        pro = self.__head
        node = self.__head.next_node
        not_found = False
        while node.data != value:
            if node.next_node is None:  # 如果已经到链表的最后一个节点,则表明该链表中没有找到执行Value值的Node节点
                not_found = True
                break
            else:
                pro = node
                node = node.next_node
        if not_found is False:
            pro.next_node = node.next_node

    def delete_last_n_node(self, n):
        """删除链表中倒数第N个节点.
        主体思路:
            设置快、慢两个指针,快指针先行,慢指针不动;当快指针跨了N步以后,快、慢指针同时往链表尾部移动,
            当快指针到达链表尾部的时候,慢指针所指向的就是链表的倒数第N个节点
        参数:
            n:需要删除的倒数第N个序数
        """
        fast = self.__head
        slow = self.__head
        step = 0

        while step <= n:
            fast = fast.next_node
            step += 1

        while fast.next_node is not None:
            tmp = slow
            fast = fast.next_node
            slow = slow.next_node

        tmp.next_node = slow.next_node

    def find_mid_node(self):
        """查找链表中的中间节点.
        主体思想:
            设置快、慢两种指针,快指针每次跨两步,慢指针每次跨一步,则当快指针到达链表尾部的时候,慢指针指向链表的中间节点
        返回:
            node:链表的中间节点
        """
        fast = self.__head
        slow = self.__head

        while fast.next_node is not None:
            fast = fast.next_node.next_node
            slow = slow.next_node

        return slow

    def create_node(self, value):
        """创建一个存储value值的Node节点.
        参数:
            value:将要存储在Node节点中的数据
        返回:
            一个新的Node节点
        """
        return Node(value)

    def print_all(self):
        """打印当前链表所有节点数据."""
        pos = self.__head
        if pos is None:
            print("当前链表还没有数据")
            return
        while pos.next_node is not None:
            print(str(pos.data) + " --> ", end="")
            pos = pos.next_node
        print(str(pos.data))

    def reversed_self(self):
        """翻转链表自身."""
        if self.__head is None or self.__head.next is None:  # 如果链表为空,或者链表只有一个节点
            return

        pre = self.__head
        node = self.__head.next
        while node is not None:
            pre, node = self.__reversed_with_two_node(pre, node)

        self.__head.next = None
        self.__head = pre

    def __reversed_with_two_node(self, pre, node):
        """翻转相邻两个节点.
        参数:
            pre:前一个节点
            node:当前节点
        返回:
            (pre,node):下一个相邻节点的元组
        """
        tmp = node.next_node
        node.next_node = pre
        pre = node  # 这样写有点啰嗦,但是能让人更能看明白
        node = tmp
        return pre, node

    def has_ring(self):
        """检查链表中是否有环.
        主体思想:
            设置快、慢两种指针,快指针每次跨两步,慢指针每次跨一步,如果快指针没有与慢指针相遇而是顺利到达链表尾部
            说明没有环;否则,存在环
        返回:
            True:有环
            False:没有环
        """
        fast = self.__head
        slow = self.__head

        while (fast.next_node is not None) and (fast is not None):
            fast = fast.next_node
            slow = slow.next_node
            if fast == slow:
                return True

        return False
#include <stdio.h>
#include <stdbool.h>

struct single_list {
	struct single_list *next;
	int val;
};

struct single_list_head {
	struct single_list *head;
};

bool is_empty(struct single_list_head *head)
{
	return head->head == NULL;
}

void dump(struct single_list_head *head)
{
	struct single_list *tmp = head->head;
	int idx = 0;

	while (tmp) {
		printf("[%02d]: %08d\n", idx++, tmp->val);
		tmp = tmp->next;
	}
}

void insert(struct single_list **prev, struct single_list *elem)
{
	if (!prev)
		return;

	if (*prev)
		elem->next = *prev;
	*prev = elem;
}

void insert_head(struct single_list_head *head, struct single_list *elem)
{
	insert(&head->head, elem);
}

struct single_list* del(struct single_list **prev)
{
	struct single_list *tmp;

	if (!prev)
		return NULL;
	if (*prev == null)
		return NULL;
	tmp = *prev;
	*prev = (*prev)->next;
	tmp->next = NULL;

	return tmp;
};

struct single_list* delete_head(struct single_list_head* head)
{
	return del(&head->head);
};

struct single_list** search(struct single_list_head* head, int target)
{
	struct single_list **prev, *tmp;

	for (prev = &head->head, tmp = *prev;
	     tmp && (tmp->val < target);
	     prev = &tmp->next, tmp = *prev)
		;

	return prev;
};

void reverse(struct single_list_head* head)
{
	struct single_list_head tmp = {NULL};
	struct single_list *elem;

	while (!is_empty(head)) {
		elem = delete_head(head);
		insert_head(&tmp, elem);
	}

	head->head = tmp.head;
}

bool is_cyclic(struct single_list_head* head)
{
	struct single_list *s1, *s2;

	s1 = s2 = head->head;

	while(s1 && s2) {
		s1 = s1->next;
		s2 = s2->next ? s2->next->next:s2->next;

		if (s1 == s2)
			return true;
	}
	return false;
}

struct single_list* middle(struct single_list_head* head)
{
	struct single_list *s1, *s2;
	struct single_list pseudo_head;

	pseudo_head.next = head->head;
	s1 = s2 = &pseudo_head;

	while (true) {
		if (!s2 || !s2->next)
			return s1;
		s1 = s1->next;
		s2 = s2->next->next;
	}

	return NULL;
};

int main()
{
	struct single_list_head head = {NULL};
	struct single_list lists[10];
	struct single_list **prev;
	int idx;

	for (idx = 0; idx < 10; idx++) {
		lists[idx].val = idx;
		lists[idx].next = NULL;
	}

	insert_head(&head, &lists[6]);
	insert_head(&head, &lists[5]);
	insert_head(&head, &lists[4]);
	insert_head(&head, &lists[1]);
	insert_head(&head, &lists[0]);

	printf("=== insert 0, 1, 4, 5, 6\n");
	dump(&head);

	prev = search(&head, 2);
	insert(prev, &lists[2]);
	printf("=== insert 2\n");
	dump(&head);

	printf("middle elem is %d\n", middle(&head)->val);

	prev = search(&head, 2);
	if ((*prev) && ((*prev)->val == 2))
		printf("The list contains 2\n");
	else
		printf("The list not contains 2\n");

	del(prev);
	prev = search(&head, 2);
	printf("After remove 2\n");
	if ((*prev) && ((*prev)->val == 2))
		printf("The list contains 2\n");
	else
		printf("The list not contains 2\n");
	dump(&head);

	printf("After reverse \n");
	reverse(&head);
	dump(&head);

	printf("middle elem is %d\n", middle(&head)->val);

	lists[0].next = &lists[6];
	printf("list is%s cyclic\n", is_cyclic(&head)?"":" not");

	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值