Python实现链表

用Python玩转数据结构

链表

节点类

根据在前学过的数据结构,那么必须有节点,Python里面没有指针的说法,所以我们用引用来实现

节点类的方法

节点类最基本的功能包括:更新数据,查询数据,更新后继节点和查询后继节点。

class Node(object):
    def __init__(self, data):
        self.data = data
        self.next_node = None

    def get_data(self):
        """
        获取当前节点的数据
        :return: 返回当前节点的数据
        """
        return self.data

    def set_data(self, data):
        """
        设置节点的数据为传入的数据
        :param data:
        :return: 无返回
        """
        self.data = data

    def get_next(self):
        """
        获取当前节带点的指向
        :return: 返回指向的对象
        """
        return self.next

    def set_next(self, next_node):
        """
        修改节点的指向
        :param next_node:
        :return:无返回
        """
        self.next_node = next_node

链表类

我们把节点类先实现,然后再来实现链表类。我们定义一个链表类,并实现初始化方法。在初始化方法中定义了两个属性,分别是headlength属性。head属性表明头节点,在初始化的时候指向None。length属性表明链表的长度,方便判断列表是否为空和返回链表的长度。

class Linked_list(object):
    def __init__(self):
        """
        初始化方法,定义一个长度属性方便统计和判断
        """
        self.head = None
        self.length = 0

链表类的方法

然后我们一个想想我们的链表类需要哪些方法,这是有链表的特性觉得的不是吗?大概列一个列表吧:

  • 在末尾插入数据
  • 在开头插入数据
  • 在指定位置插入 数据
  • 修改指定位置的数据
  • 获取指定位置的数据
  • 查找数据在链表中的数据
  • 删除指定位置的数据
  • 展示整个链表
  • 删除整个链表
  • 判断这个链表是否为空

大概就是这些看起来是不是有点多,但是不用担心,我们一点一点的来实现。

判断链表是否为空

首先是判断一个链表是否为空,这个很好实现,我们先看代码:

    def isEmpty(self):
        """
        判断链表是否为空,如果为空返回True,否则为False
        :return:
        """
        return self.length == 0

除去注释也就一行代码的事情,用链表的长度和0做一个比较,不相等返回False相等返回True然后就能很好的判断了。

获取链表的长度

同样的获取链表的长度也很简单:

    def get_length(self):
        return self.length
在链表末尾插入数据

然后就是在末尾插入数据

    def append(self, data_or_node):
        """
        在链表的末尾增加一个节点
        :param data_or_node:
        :return:
        """
        item = None
        # 这一个判断是为了判断传进来的数据是什么类型的
        if isinstance(data_or_node, Node):
            item = data_or_node
        else:
            item = Node(data_or_node)

        if not self.head:
            self.head = item
            self.length += 1
        else:
            node = self.head
            while node.next_node:
                node = node.next_node
            node.next_node = item
            self.length += 1

首先是判断传入进来的数据是什么类型,如果已经是一个节点类的话我们就不用转换,如果不是,我们就将其转换为节点类。这里用到了isinstance()方法,这个方法的作用是判一个对象是否是一个已知的类型,考虑继承的情况。然后判断这个链表的头结点是否存在,也可以通过判断链表的长度来实现。如果头节点不存在,就将这个节点变为头节点。所以有了这个引用,将item的值赋值给head。如果不是的话我们首先就要找到尾节点,通过头节点开始循环,一直找到尾节点,然后引用。不管是那种情况都不能忘记将链表的长度加上1

在链表的头部插入数据

头部插入数据也很简单,

    def add(self, data_or_node):
        """
        在链表的最前方插入一个数据
        :param data_or_node:
        :return:
        """
        if isinstance(data_or_node, Node):
            item = data_or_node
        else:
            item = Node(data_or_node)

        if not self.head:
            self.head = item
            self.length += 1
        else:
            next_node = self.head
            self.head = item
            item.next_node = next_node
            self.length += 1

大部分都差不多,只是在判断不是空链表之后的处理有点不一样。我们将之前头节点的值赋值给一个变量,然后将我们传入的变量指向给原头节点,同时将头节点指向我们传入的变量。

在指定位置插入节点

我们可以通过指定位置插入节点这个是很必要的一个方法,先看代码:

    def insert(self, index, data_or_node):
        """
        根据给定的索引在链表中插入一个节点
        :param index: 索引
        :param data_or_node: 数据
        :return: 无返回
        """
        if self.isEmpty():
            print("this Linked list is Empty")
            return

        if index < 0 or index >= self.length:
            print("error: out of index ")
            return
        item = None
        if isinstance(data_or_node, Node):
            item = data_or_node
        else:
            item = Node(data_or_node)

        if index == 0:
            self.add(item)
            return

        j = 0
        node = self.head
        prev = self.head
        while j < index:
            prev = node
            node = node.next_node
            j += 1
        prev.next_node = item
        item.next_node = node
        self.length += 1

对于所有给出索引的方法来说,我们都需要判断这个链表是否为空和给定的索引是否越界。然后再把传入的节点通过判断转换为节点类。如果索引的值是0的话就是在头部插入,我们直接调用在前面定义的内部方法就好,然后记得return结束函数,不然那你会将你的所有的节点的值都改变。然后就是关键的步骤,我们定义三个变量,作用分别是:计数器、记录前置节点和记录当前节点。最开始的节点都为头节点,然后开始循环,当j小于索引值的时候呢,循环,同时将node的值赋值给prev,将节点记录下来。当找到插入节点的位置的时候,我们将prev指向我们传入的节点,然后将传入的节点再指向给item就完成在指定位置插入。只要是涉及到节点个数的变化的操作的时候一定要记得对length进行对应的操作。

删除指定位置的节点

说完了增加,按照惯例就是删除了。首先是删除指定位置的节点:

    def delete(self, index):
        """
        根据给定的索引删除一个节点
        :param index: 索引
        :return: 无返回
        """
        if self.isEmpty():
            print("this Linked list is Empty")
            return

        if index < 0 or index >= self.length:
            print("error: out of index ")
            return

        if index == 0:
            self.head = self.head.next_node
            self.length -= 1

        j = 0
        node = self.head
        prev = self.head
        while j < index:
            prev = node
            node = node.next_node
            j += 1
        prev.next_node = node.next_node
        self.length -= 1

首先还是几个判断,然后如果是删除头节点,我们将头节点的值指向头节点的下一个。如果不是,就和插入很像了,找到索引对应的位置,然后将prev直接指向node的下一个,然后链表长度减1。

删除整个链表

删除了一个节点就像删除整个链表,这个代码也超级简单:

    def clear(self):
        """
        清除链表
        :return: 无返回
        """
        self.head = None
        self.length = 0

我们只需要将链表的头节点再指向为空,然后长度变为0就好。

修改指定位置的节点的值

删除也很简单,下面来说修改,修改指定位置节点的值

    def updata_node_item(self, index, data):
        """
        根据给出的索引更新一个节点的值
        :param index: 索引
        :param data: 更新的值
        :return: 无返回
        """
        if self.isEmpty():
            print("this Linked list is Empty")
            return

        if index < 0 or index >= self.length:
            print("error: out of index ")
            return

        j = 0
        node = self.head
        while j < index:
            node = node.next_node
            j += 1
        node.data = data

就只是常规的判断然后是循环找到节点后修改值就好。

根据索引获得对应节点的值

查找是一个很重要的功能,有两种需要我们实现,先来第一种:根据索引查找对应的值:

 def get_node_item(self, index):
        """
        根据索引获取获取节点的值
        :param index: 索引
        :return: 返回索引对应节点的值
        """
        if self.isEmpty():
            print("this Linked list is Empty")
            return

        if index < 0 or index >= self.length:
            print("error: out of index ")
            return

        j = 0
        node = self.head
        while j < index:
            node = node.next_node
            j += 1
        return node.data

和修改很类似,不同的是我们直接返回值就好

根据值查对应的节点的索引

我们也可以传一个值然后找对应的索引,这样就能做到:

    def get_node_index(self, data):
        """
        查找一个数据的索引
        :param data:数据
        :return:索引
        """
        node = self.head
        j = 0
        while node.next_node:
            if node.data == data:
                return j
            else:
                node = node.next_node
            j += 1
        if j == self.length:
            print("%s is not found " % str(data))

其实都差不多,但是呢有一种情况需要注意,就是如果当j的值都等于链表的长度的时候说明,链表当中没有这个值。

展示链表

其实到这里就差不多了,然后就是一个展示整个链表的方法:

    def show(self):
        """
        展示链表
        :return:
        """
        if self.isEmpty():
            print("this is a empty linked list")
        n = 0
        node = self.head
        while n < self.length:
            print("node %d : %s" % (n, node.data))
            node = node.next_node
            n += 1

试试效果

mylinked = Linked_list()
for i in range(10):
    mylinked.append(i)
mylinked.show()
print(mylinked.get_length())
print(mylinked.get_node_index(4))
print(mylinked.get_node_item(5))
print(mylinked.updata_node_item(4, 20))
print(mylinked.delete(8))
print(mylinked.insert(0, 420))
mylinked.show()

结果:

node 0 : 0
node 1 : 1
node 2 : 2
node 3 : 3
node 4 : 4
node 5 : 5
node 6 : 6
node 7 : 7
node 8 : 8
node 9 : 9
10
4
5
None
None
None
node 0 : 420
node 1 : 0
node 2 : 1
node 3 : 2
node 4 : 3
node 5 : 20
node 6 : 5
node 7 : 6
node 8 : 7
node 9 : 9

Process finished with exit code 0

完整代码传送门:点这里

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,362评论 5 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,330评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,247评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,560评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,580评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,569评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,929评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,587评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,840评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,596评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,678评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,366评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,945评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,929评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,165评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,271评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,403评论 2 342

推荐阅读更多精彩内容

  • 搞懂单链表常见面试题 Hello 继上次的 搞懂基本排序算法,这个一星期,我总结了,我所学习和思考的单链表基础知识...
    醒着的码者阅读 4,572评论 1 45
  • ORA-00001: 违反唯一约束条件 (.) 错误说明:当在唯一索引所对应的列上键入重复值时,会触发此异常。 O...
    我想起个好名字阅读 5,141评论 0 9
  • 一、概念梳理 链表是计算机科学里面应用应用最广泛的数据结构之一。它是最简单的数据结构之一,同时也是比较高阶的数据结...
    黑加仑妞阅读 4,059评论 0 0
  • 摘自《维基百科》 链表(Linked list)是一种常见的基础数据结构,是一种线性表,但是并不会按线性的顺序存储...
    ChinaChong阅读 1,682评论 0 52
  • 一些概念 数据结构就是研究数据的逻辑结构和物理结构以及它们之间相互关系,并对这种结构定义相应的运算,而且确保经过这...
    Winterfell_Z阅读 5,646评论 0 13