数据结构(1)---单链表定义和操作

数据结构(1)—单链表定义和操作

首先,需要了解变量存储的是数据的地址指向

a = 10  # a的空间指向10 ,存10的地址
b = 20
c = "aaa"
a, b = b, c  # 从右边开始,找到b的存储地址,把地址引用导向改变一下
print(a, b)   # 20 aaa

1.如何定义链表一个节点

node: 一个节点是一个类,
需要存储: 元素域和地址域

class Node(object):  # 节点
    def __init__(self, elem):
        self.elem = elem  # 存储元素值

2.如何定义一个单链表

对于单链表,唯一知道的信息就是头结点,所以生成单链表对象后,必须要init一个头结点

class SingLinkList(object):
    """
    单链表: 数据分配的内存可以不连续,存储方便
    """

    # 必须存在一个对象,指向头结点
    def __init__(self, node=None):
        # 初始化头结点为None  指向一个空链表,如果先初始化Node,则  把节点信息指给头结点    参数为node   _head接收
        self._head = node

2.1 判断链表是否为空

链表为空,即头结点指向的节点为None

    def is_empty(self):
        """
        是否为空,直接看头结点指的node是不是none
        :return:
        """
        return self._head == None

2.2 计算链表的长度

思路:
从头结点开始遍历,count为1,
cur后移,不为空的话,继续+1 ,一直后移,遍历,累加,直到cur指向None
注意:
因为循环体是 cur=cur.next
所以要判断 cur !=None 是循环条件,而不是 cur.next, 计数不要错了

    def length(self):
        """链表长度. 特别注意: 判断是 cur.next还是cur 为空
        思路: 定义当前节点 cur
        移动当前节点   cur=cur.next
        判断终止条件   cur=none
        """
        cur = self._head
        count = 0
        while cur != None:
            count += 1
            cur = cur.next
        return count

2.3 链表遍历

当cur!=None时,
遍历移动元素:cur =cur.next

    def travel(self):
        """遍历"""
        cur = self._head
        while cur != None:
            print(cur.elem, end=" ")
            cur = cur.next

2.4 头插法:每次头部插入元素 (考虑逆序链表的建立可用此方法)

时间复杂度:0(1)
思路:
(1)定义node
(2) 头结点的指向赋值给node的下一个节点

如何保证后面的链表不乱:
先定义node.next =_head
再把头结点的指向 node
注意:需要校验看当链表为空时,是否符合条件
如果不清楚是否包含,先用条件类的写一遍

 def add(self, item):
        """时间复杂度:0(1)"""
        """头部添加:头插法"""
        node = Node(item)
        node.next = self._head  # 先将头结点的指向赋值给node的下一个节点
        self._head = node  # 将node
        #下面是没有合并条件时的写法
        # if self.is_empty():
        #     self._head=node
        # else:
        #     #  注意顺序问题:  1.先把新节点的地址存 原来链表的首结点 (要不然之前的链表丢失了)  2.再把头部节点指向新节点
        #     cur=self._head
        #     cur=node
        #     cur.next=cur.next.next

2.5 尾插法

时间复杂度:O(n)
思路: 先移动到尾部,再插入
特殊情况考虑:
(1)链表为空,head直接指向即可
(2)链表不为空,设置cur后一直后移,注意这里判断 cur.next,即下一个元素是否为空,一直到 为None,然后赋值 给 cur.next

 def append(self, item):
        """时间复杂度:0(n):从头到尾部遍历"""
        """尾部添加:一直走到尾节点,然后传入数据,尾插法------"""
        # 先把要添加的节点初始化
        node = Node(item)
        # cur = self._head
        # 注意:
        if self.is_empty():
            print("当前节点为空!!插入元素")
            self._head = node

        else:
            # 先进行遍历,一直到最后一个元素
            cur = self._head
            while cur.next != None:  # 如果cur为空,则cur.next直接报错了,所以需要考虑特殊情况
                cur = cur.next
            # 在最后一个节点的下一个节点插入
            cur.next = node

2.6 任意位置插入

时间复杂度:O(n)
特殊情况考虑:
(1)pos为负数,或者为0,1 之类的,采用头插法
(2)pos大于元素长度,注意不包括长度,采用尾插法
(3)其他情况: 比如pos为2,则需要插入到原来的 1和2之间,即要在pos的前一个位置拦截,cur游标到要插入位置的前一个元素
赋值: 先考虑node的next : node.next = cur.next
然后考虑cur.next=node

    def insert(self, pos, item):
        """时间复杂度:0(n),考虑最差情况"""
        """指定位置插入,注意第一个元素pos是1"""
        # 考虑如果插入的位置小于0或等于0 ,头插法
        if pos <= 1:
            self.add(item)
        # 如果插入的位置大于链表长度,进行尾插法,注意不包括最后一个位置
        elif pos > self.length():
            self.append(item)
        else:
            node = Node(item)
            cur = self._head
            #  要在pos的前一个位置拦截,比如3,要3的位置后移,cur游标到要插入位置的前一个元素,因为后执行next,所以还要前移一个位置
            for i in range(pos - 2):
                cur = cur.next
            node.next = cur.next  # 将cur的下一个元素指向 node的下一个元素
            cur.next = node  # 将node指向cur的下一个元素

        """
        或者使用while实现:
            count = 0
            # pre用来指向指定位置pos的前一个位置pos-1,初始从头节点开始移动到指定位置
            pre = self._head
            while count < (pos-1):
                count += 1
                pre = pre.next
            # 先将新节点node的next指向插入位置的节点
            node.next = pre.next
            # 将插入位置的前一个节点的next指向新节点
            pre.next = node
        
        
        """

2.7 删除元素

时间复杂度:O(n)
思路:
采用两个指针,pre和cur ,cur要比pre先移动一步
当找到元素后,pre.next = cur.next 即可,即当前cur被销毁
考虑的特殊情况:
1.第一个节点就是要删除的
(1)只有一个元素 : 当前元素是cur和头结点指向一样,那么头结点指向 cur的next , 注意:这里写 cur=cur.next是不对
cur 和 self._head不一样
如果 cur =self._head,那么 self._head = cur.next
(2)不止一个元素
2. 要删除的是最后一个元素
cur 是最后一个元素,则 cur=cur.next 即为None

 def remove(self, item):
        """移除元素: 先遍历查找元素是否存在,存在的话就remove"""
        # 方法1: 使用两个指针   一个pre  一个cur  cur一定要比pre先移动一个节点,所以pre先移动到cur的位置,cur再往后移动
        cur = self._head
        pre = None
        while cur != None:
            #  需要考虑的特殊情况:1.第一个节点就是要删除的    (1)只有一个元素  (2)不止一个元素  2. 要删除的是最后一个元素
            # 如果当前的元素是要删除的元素
            if cur.elem == item:
                # 删除元素用两个指针或一个指针都可以  方法1:一个指针是 cur=cur.next   (将下一个指针指向当前指针)  方法2 :如下
                #  此时需要判断需要删除符合要求的是不是头结点,如果是,不需要pre节点了
                if cur == self._head:
                    # cur=cur.next   # 注意:执行这行的时候,删除失败   为啥是self._head= cur.next  有啥区别?????
                    self._head = cur.next
                else:
                    pre.next = cur.next  # 即cur指向的下一个元素指向 pre.next  cur这个元素删除掉

                break  # 注意,如果不break  会重复执行if 和else
            # 当cur 不是要查找的元素,pre和 cur  都后移
            else:
                pre = cur
                cur = cur.next

2.8 查找一个元素

时间复杂度:O(n)
思路:
从cur开始遍历,看cur.elm是不是和目标值相等
分支:
1.头结点就是要找的元素: 直接返回true
2.头结点不是:
循环到cur.next 继续判断
(1)是 返回true
(2) 不是 移动cur=cur.next
如果循环完毕还是没有,返回false

 def search(self, item):
        """时间复杂度:0(n):需要遍历"""
        """查找元素:遍历比对数据,如果游标所指数据是,返回true"""
        cur = self._head
        # 先判断头结点是不是等于item
        if cur.elem == item:
            return True
        else:
            # 如果头结点的下一个节点不为none  ,
            while cur.next != None:
                if cur.elem == item:
                    return True
                else:
                    cur = cur.next
            return False  # 注意当遍历后都没有后,要返回false
        #  注意:此处把if去掉,也是验证成功的,因为直接不符合while,直接返回false

最后调用和执行如下:

if __name__ == "__main__":
    ll = SingLinkList()
    print(ll.is_empty())  # True
    print(ll.length())

    ll.append(1)
    print(ll.is_empty())  # True
    print(ll.length())
    ll.add(8)  # 头插法插入8
    ll.append(2)
    ll.append(3)
    ll.append(4)
    ll.append(5)
    ll.insert(9, 7)
    ll.insert(0, 100)  # 100 8 1 2 3 4 5 7
    ll.insert(2, 200)  # 100  200 8 1 2 3 4 5 7
    ll.insert(1, 400)  # 400 100 200 8 1 2 3 4 5 7

    a = ll.search(999)  # False
    print(a)
    ll.remove(100)
    ll.travel()  # 400 200 8 1 2 3 4 5 7
    ll.remove(400)
    ll.travel()   # 200 8 1 2 3 4 5 7
    ll.remove(7)
    ll.travel()   # 200 8 1 2 3 4 5

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值