1.1 线性表

1.1.1 顺序表和链表都是线性表的一种。那么什么是线性表?

线性表:同类型数据元素有序的线性(一维) 结构。

它是一种数据结构:

①名字:线性表

②对象:n个元素构成的有序序列

③操作:查找,删除,插入,添加。

1.1.2 线性表的存储实现方式1----顺序表

1.1.2.1

在用python实现顺序表的各种操作之前,我们需要学习python已有的顺序表list,有助于我们写出自己的顺序表!

list的实现技术(有一体式与分离式两种)

  • python中的标准list顺序结构已十分成熟,它的元素个数是可变的,没有指定的上限。(在加入元素的时候,通过每次扩容1.125倍,来实现末端加入元素的复杂度为 O(1)
  • 基于引索的高效访问(O(1))
  • 允许加入任意多的元素,并且保持表对象的id不变(就是始终能找到这个表的位置,后面我们会知道,这是得益于表对象标识与元素内容分离式技术)

基于以上,list采用顺序+分离式技术实现。

list的操作使用

  • len() 得到表的长度,复杂度是O(1),因为它是由表对象标识记录
  • pop(i)删除操作只有末端为O(1),其余为O(n)
  • b = a.clear() 表示清除表a 结果为b。O(1)
  • a = [1,2,3,4,5]  引索是0--4  末尾引索也可以是-1 用[  起始 : 结束 : 步长  ]来切片
  • 复合项 b = [1,2,a,4]       b[2][1]可得到a里面的元素 2
  • 允许  list1+list2

1.1.2.2 自己动手实现一个顺序表

class Ordlist():
    
    def __init__(self,capacity,num):
        self._cap = capacity # 表容量
        self.num = num     # 元素数量
        self.list = [None]*self._cap # 初始化顺序表

    def print_list(self):
        if self.is_empty():
            mylist=[]
        else:
            mylist = self.list[0:self.num]
        print(mylist)

    def is_empty(self):
        return self.num == 0

    def is_full(self):
        return self._cap == self.num

    def append_elem(self,elem):
        if not self.is_full():
            self.list[self.num] = elem
            self.num += 1
        else:
            return "list is full"

    def prepend_elem(self,elem):
        if not self.is_full():
            self.list[1:] = self.list[0:-1]
            self.list[0] = elem
            self.num += 1
        else:
            return "list is full"

    def insert(self,index,elem):
        if not self.is_full():
            list_end = self.list[index:]
            self.list[index] =elem
            self.list = self.list[0:index+1]+list_end
            self.num += 1
        else:
            return "list is full"
        
    def del_end(self):
        if not self.is_empty():
            self.list[self.num-1] = None
            self.num -= 1
        else:
            print("list is empty")

    def del_index(self,i):
        if not self.is_empty():
            self.list=self.list[0:i]+self.list[i+1:]+[None]
            self.num -= 1
        else:
            print("list is empty")
        
        

# https://blog.csdn.net/Tommy1295/article/details/82315768


'''一个表的操作:
1.是否空
2.是否满
3.后端添加
4.前端添加
5.中间添加
6.后端删除
7.前端删除
8.任意位置删除
9.遍历
10.查找某元素第一次出现的位置'''

1.1.3 线性表的存储实现方式2----链表

1.1.3.1

线性表的另外一种实现方式:表现在物理层面的散乱性,每个元素带有下一个元素的地址,对内存见缝插针。

单链表实现技术

  • 最普通的链表形式:每个元素都是一个带有元素值和下一个节点位置信息的节点。找到里首节点,就能找到链表中的任何元素。所以,我们需要一个变量保存首节点的位置,这个变量称之为表头变量。

单链表的操作使用

  • 创建空链表:把表头变量设置为空链接(None) O(1)
  • 删除链表:将表头变量设置为空(None),python自动管理内存O(1)
  • 判断表是否为空:表头变量是否为None, 另外注意:链表不会满O(1)
  • 首端插入:新增节点> 把原来首节点的位置给新增节点的 next位置存放 > 修改表头变量指向新增节点
  • 一般插入:找到pre的节点 >把pre节点的next位置的值给新节点的next的位置 >把新增节点的位置信息给pre的next的位置
  • 删除首元素:修改表头变量使其指向第二个节点位置
  • 删除一般元素:找到pre节点 > 修改pre节点的next值使其指向下下个节点
  • 定位,遍历元素:p = head

按下标:

while p is not None and i>0:

    i -= 1

    p = p.next(p变量找到了指定的第i个元素的位置)

按元素值查找:

while p is not None and p != value:

    p = p.next

当然,如果存在值相同的多个元素,那么就只能找到第一个。

 

1.1.3.2 自己动手实现一个链表

#实现的关键是 节点(元素、引索)的构建
# 内部逻辑和属性的严谨关联
# 引用类型可修改原类型


class Note:
    def __init__(self,elem,next_=None):      # 节点单元
        self.next = next_  # 引索
        self.elem = elem  # 元素

class Linklist:
    def __init__(self):   # 创建空链表
        self.head = None
        self.length = 0

    def delete(self):  # 删除链表
        self.head = None

    def traversal(self):
        p = self.head
        elems=[]
        while p != None:
            elems.append(p.elem)
            p=p.next
        print(elems)

    def prepend(self,elem): # 首部添加元素
        
        note = Note(elem)
        note.next=self.head
        self.head= note
        
        self.length += 1

    def insert(self,elem,i): # 插入元素
        note = Note(elem)
        if 0<i<=self.length:
            p=self.head
            p0=p
            while i>0:
                i -= 1
                p0=p
                p=p.next
            p0.next=note
            note.next=p

            self.length += 1
        elif i==0:
            self.prepend(elem)
        else:
            print("超出索引")
            
    def append(self,elem): # 末端加入元素
        self.insert(elem,self.lenght)
    

    def delete(self,i): # 删除第i个元素
        if 0<i<self.length:
            p=self.head
            p0=p
            while i>0:
                i -= 1
                p0=p # i 前端元素
                p=p.next # i 本身
                
            p0.next=p.next
            self.length -= 1
        elif i == 0 and self.length>0:
            self.head=self.head.next
            self.length -= 1
        else:
            print("索引出错")

我认为,实现链表的关键是要清楚:

  • 链表的最小单元是节点,所以要先定义一个节点类Lnote。
  • 然后再是理解head 指代的是一个表头节点,并且,next的指向也是一个节点类型,这个类型里面有元素和下一个元素的地址(其实就是下个元素)
  • 任何遍历都要从 p =head 开始,用这个节点是否为空作为循环条件 ,更值得一提的是配合i >0 的循环条件,i -= 1操作,来定位第i个元素

几点改进:

def list_elements(self):   #  就是一个生成器了,生成器会自动提供遍历机制(相当一个列表了)比较规范   作用:可以给其他操作使用
        p = self._head
        while p is not None:
            yield p.elem
            p = p.next_


def print_me(self):
        for i in self.list_elements():
            print(i)


def  find_it(self,n):  #  找到第一个值,第二个为n的元素?利用生成器阿
        for i ,j in enumerate(self.list_elements()):  # 生成器可以看成 既定列表 牛逼
            if n == j :
                print(i)

1.1.4 练习题

 

1.1.4.1 单链表的简单变形一(表头加一个尾部节点的地址,闭合这个单链表)

实现代码:

from Llist import *

""" 因为增加了尾节点信息,只是对部分操作的改变"""
# 这个程序改进了尾部操作的效率
class endlist(Linklist):
    def __init__(self):
        super().__init__()
        self._rear = None  # 注意尾部信息和头部信息一样是全局变量

    def prepend(self,elem): # 前端加入操作
        note = Note(elem)
        if self.head is None:
            self.head = note
            self._rear = self.head
        else:
            self.head=Note(elem,self.head)

    def append(self,elem):
        note=Note(elem)
        if self.head is not None:
            self._rear.next = note
            self._rear = note
        else:
            self.head = note
            self._rear = self.head
        self.length += 1

 

1.1.4.2 单链表的简单变形二(循环单链表)

实现代码:

#循环单链表(记录尾部的)
class Note:
    def __init__(self,elem,next_=None):
        self.next =next_
        self.elem=elem

class looplist:
    
    def __init__(self):
        self._rear = None  # 这是尾部循环单链表唯一的全局变量
        

    def prepend(self,elem):  # 参照图来写逻辑
        if self._rear ==None:
            self._rear = Note(elem)
            self._rear.next=self._rear
        else:
            note=Note(elem,self._rear.next)
            self._rear.next=note
            

    def traversal(self):
        p=self._rear
        if p is None:
            print([])
        else:
            p0=p
            p=p.next
            while p is not self._rear:
                print(p.elem)
                p=p.next
            print(p0.elem)

 

1.1.4.3 单链表的变形三(双链表)

实现代码:

class Note:
    def __init__(self,elem,prev=None,next_=None):
        self.elem=elem
        self.next=next_
        self.prev=prev

class Dlist:
    def __init__(self):
        self.head=None
        self._rear=None

    def prepend(self,elem):#首端加入
        if self.head==None:
            self.head=Note(elem)
            self._rear=self.head
        else:
            note=Note(elem,None,self.head)
            self.head.prev=note
            self.head=note
     
    def traversal(self):
        p=self.head
        while p is not None:
            print(p.elem)
            p=p.next

1.1.4.4 单链表的简单变形四(循环双链表)

代码实现:

class Note:
    def __init__(self,elem,prev=None,next_=None):
        self.elem=elem
        self.next=next_
        self.prev=prev

class Dlist:
    def __init__(self):
        self.head=None
        self._rear=None
        self.length=0

    def prepend(self,elem):#首端加入
        if self.head==None:
            self.head=Note(elem)
            self._rear=self.head
            self.head.prev=self.head
            self.head.next = self.head
        else:
            note=Note(elem,None,self.head)
            self.head.prev=note
            self.head=note
            self.head.prev=self._rear
            self._rear.next=self.head
        self.length +=1

    def append(self,elem):# 测试后端加入
        if self.head==None:
            self.prepend(elem)
        else:
            note=Note(elem,self._rear,self.head)
            self._rear.next=note
            self._rear=note
            self.length +=1

    def pop(self,index): #测试弹出功能
        if self.length ==1:
            ans=self.head.elem
            self.head=None
            self._rear=None
            
        elif self.length==2:
            if index==0:
                ans=self.head.elem
                self.head=self._rear
                self._rear.prev=self._rear
                self._rear.next=self._rear
                
            elif index==1:
                ans=self._rear.elem
                self._rear=self.head
                self.head.prev=self.head
                self.head.next=self.head
                
            else:
                print("超出引索1")
                self.length +=1
            
        elif 0<=index<self.length:
            if index==0:
                ans=self.head.elem
                self.head.next.prev=self._rear
                self.head=self.head.next
                self._rear.next=self.head
            elif index==self.length-1:
                ans=self._rear.elem
                self._rear=self._rear.prev
                self._rear.next=self.head
                self.head.prev=self._rear
            else:
                p=self.head
                while index:
                    index -=1
                    p=p.next
                ans=p.elem
                p.prev.next=p.next
                p.next.prev=p.prev
            
        else:
            
            self.length +=1
            return print("超出引索2")
        
        self.length -=1
        #print("长度",self.length)
        return ans
            
     
    def traversal(self):
        p=self.head
        while p is not self._rear:
            print(p.elem)
            p=p.next
        print(p.elem)

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值