zoukankan      html  css  js  c++  java
  • 数据结构——链表

    1.什么是链表

     链表是一种物理存储结构上非连续、非顺序的存储结构,数据元素的逻辑顺序是通过链表中的指针链接次序实现的。

    2.节点

    节点维护变量data和next,分别用于存储数据和指向下一个节点。

    C#:

    class Node<T>
    {
        private T data;
        private Node<T> next;
    
        public T Data
        {
            get { return data; }
            set { data = value; }
        }
    
        public Node<T> Next
        {
            get { return next; }
            set { next = value; }
        }
    
        public Node(T item)
        {
            data = item;
        }
    
        public Node(T item, Node<T> nextNode)
        {
            data = item;
            next = nextNode;
        }
    }

     Python:

    class Node:
        def __init__(self, data, next=None):
            self.data = data
            self.next = next
    
        def __repr__(self):
            return str(self.data)

    3.链表类

    正式构建一个链表类

    链表类应该具有以下属性或方法:

    count 元素数量
    head 头节点指针
    insert 向指定位置插入元素
    get/set 获取/修改节点值
    remove 删除指定元素

    定义好一个类:

    C#:

    class LinkList<T>:
    {
        private Node<T> head;//头节点
        private int count;//计数器
    }

    Python:

    class LinkList:
        def __init__(self):
            self._head = None
            self.count = 0

    3.1 获取链表元素个数

    由于我们维护了一个属性,所以直接返回count即可

    class LinkList<T>:
    {
        ...
        public int Count { get { return count; } }
    }

    在Python中我们重写__len__方法

    def __len__(self):
        return self.count

    3.2 获取指定位置节点

    类比索引功能,设定头节点位置为0,下一节点位置+1

    实现一个函数,返回指定位置的Node对象,两种直观解决思路:

    迭代,通过一个while或循环来遍历至指定位置

    递归,链表是一种天然适合递归的数据结构,通过递归也可以轻松找到目标节点

    C#(迭代实现):

        private Node<T> GetIndexNode(int index)
        {
            //获取指定索引的Node对象
            if (index > count)
            {
                throw new IndexOutOfRangeException();
            }
    
            Node<T> target = head;
            for (int i = 0; i < index; i++)
            {
                target = target.Next;
            }
    
            return target;
        }

    Python(递归实现):

    def _getnode(self, index: int):
        # 返回指定索引的节点
        def getnode(node: Node, offset: int):
            '''递归函数
            :param node: 节点
            :param offset: 偏移量
            :return:
            '''
            if not node or offset < 0:
                raise IndexError('超出索引范围')
            if offset == 0:
                return node
            return getnode(node.next, offset-1)
        return getnode(self._head, index)

     Tips:使用递归实现可能会出现栈溢出,python默认调用栈容量1000

    3.3 向指定位置插入一个节点

    向位置i插入一个节点,只要找到目标位置前一个节点(i-1),把i-1节点的next指向新节点,再把新节点的next节点指向i+1节点即可

    特别要注意如果向0位置插入节点,要修改head的指向

    C#:

        public void Insert(T item, int i)
        {
            if (i == 0)
            {
                Node<T> newNode = new Node<T>(item,head);
                head = newNode;
            }
            else
            {
                Node<T> preNode = GetIndexNode(i - 1);
                Node<T> newNode = new Node<T>(item, GetIndexNode(i));
                preNode.Next = newNode;
            }
    
            count++;
        }

    Python:

    def insert(self, item, index):
        if index > self.size:
            raise IndexError
        if index == 0:
            node = Node(item, self._head)
            self._head = node
        else:
            prev = self._getnode(index - 1)
            node = Node(item, prev.next)
            prev.next = node
        self.count += 1

    3.4 访问/修改一个节点的值

    前面已经实现了查找节点的访问,现在只用提供一个接口get/set节点存储的值即可

    C#:

    public T GetItem(int i) {  return GetIndexNode.Data; }

    Python:

    def __getitem__(self, item):
        if not isinstance(item,int):
            raise IndexError
        return self._getnode(item).data
    
    def __setitem__(self, key, value):
        if not isinstance(key,int):
            raise IndexError
        node = self._getnode(key)
        node.data = value

    3.5 删除节点

    实现pop和remove两个方案,分别使用索引位置和节点值来删除一个节点

    C#:

        public T Pop(int i)
        {
    
            if (i == 0)
            {
                Node<T> target = head;
                head = target.Next;
                count--;
                return target.Data;
            }
    
            T response = GetIndexNode(i).Data;
            Node<T> preNode = GetIndexNode(i - 1);
            Node<T> nextNode = GetIndexNode(i).Next;
            preNode.Next = nextNode;
            count--;
            return response;
        }
    
        public void Remove(T item)
        {
            if (head.data = item) {
                head = head.next;
            } else {
                Node<T> prev = Node(None,head);
                while (prev.next != None) {
                    Node<T> cur = prev.next;
                    if (cur.data == item) {
                        prev.next = cur.next;
                        break;
                    }
                }
            count--;
        }

    Python:

    def pop(self, index: int):
        # 根据索引位置删除链表节点
        prev = self._getnode(index - 1)
        target = prev.next
        prev.next = target.next
        self.count -= 1
        return target.data
    
    def remove(self, item):
        # 根据节点值删除链表节点
        prev = Node(None, self._head)
        while prev.next != None:
            if prev.next.data == item:
                self.count -= 1
                prev.next = prev.next.next
                return
            prev = prev.next

    4 性能分析和优化思路

    实际上,我们实现的增删改查大多基于GetIndexNode方法,时间复杂度随元素的位置而定,如果操作在链表头,那么时间复杂度O(1),如果操作在链表尾,时间复杂度则O(n),整体来看增删改查的时间复杂度均为O(n)。

    与数组对比,由于数组是一个在内存连续存放的数据结构,所以数组支持随机访问(任意索引访问时间复杂度均为O(1))。在随机读写性能上数组会比链表有更好的表现,但是链表也有优点,链表是一个实现了自动扩容的数据结构,我们完全可以不去关心一个链表能容纳多少元素,而数组则往往来通过扩容来实现动态数组,也会造成空间和时间的浪费。

    实际上,链表一般只在尾部添加元素,我们完全可以再维护一个foot变量指向链表尾来优化效率。

    5. 其他

    上面我们实现了一个最基础的链表,实际上链表有不同的许多形式,还有双向链表、循环链表等等,这些链表都是基于单向链表发展出来的,有兴趣的朋友可以亲自一一实现。

  • 相关阅读:
    在C#中实现高性能计时[转]
    序列化(Serializable)的学习
    日常常用英语
    使用javascript灵活控制DIV的位置
    酷我创始人雷鸣:程序员个人成长的四个要素
    String.Format格式说明
    各种类型转换的比较
    学好计算机英语
    AS关键字进行类型转化的优点以及限制
    Spoken English美国人常用的英语口语
  • 原文地址:https://www.cnblogs.com/lazyfish007/p/11815944.html
Copyright © 2011-2022 走看看