2-线性结构

2020-05-11  本文已影响0人  往事有痕

线性结构的概念

三种主要线性数据结构类型

列表-LIST

顺序存储(数组实现)

数组两大特点

你所要了解的数组操作

增加数组大小
  1. 创建一个新的,更大的数组(申请更大的内存空间)
  2. 将原数据复制到新的数组中(这样新数组就有更大的空间容纳数据项)
  3. 将指向就数组的变量重新指向新数组,旧数组的内存则被回收
减小数组的大小
  1. 创建一个更小的数组
  2. 将原数组内容复制到新数组
  3. 将新数组设置为新的数组对象
数组中插入一项
  1. 先检查数组大小是否够用,不够用先申请空间
  2. 从数组最后一项的位置开始直到要插入数据项的索引位置,将每个数据项都往后移动一个位置
  3. 这样就会在目标位置空出一个位置给新的数据项插入
数组中删除一项
  1. 先将目标索引位置的数据项从数组中移除
  2. 将索引位置之后的所有数组向前移动一位

时间复杂度

# O(1)
搜索第i项/替换第i项/从末尾追加/从末尾删除/
# O(n)
从第i个位置插入/删除/增加数组容量/减小数组容量

链式存储(链表实现)

特点

构建节点类

# 构建节点类
class Node:
  def __init__(self, data, next=None):
    self.data = data
    self.next = next
# 一般来讲,初始化次类会创建一个空链接或是新的Node对象

生成链表

head = None
for i in range(n):
  head = Node(i, head)
# 假设n=3,生成的链表是这个样子的:2->1->0->None,此时head=Node(2,指向数据项为1的节点)
# 你会发现,数据项的遍历和节点生成以相反的方式呈现(当然,这只是一种生成链表的方式,也可以通过程序设计,保证数据项和节点生成的一致性)

链表操作(代码实现)

遍历
# 正确的做法一般来讲,会构建一个新的变量,初始化为链表的head指针,用指针去遍历整个链表
temp = head
while temp !=None:
  temp = temp.next

# 错误的遍历方法,因为head在变化,最后的结果就是,head是None,前面的节点全都删除了
while head !=None:
  head = head.next
搜索目标值
# 构建一个新的变量,初始化为链表的head指针,通过遍历搜索目标值
temp = head
while temp != None and temp.data != target_data:
  probe = probe.next
if probe != None:
  print("success to find")
 else:
  print("fail to find")
替换第i项位置的数据
temp = head
# 假设替换索引为5的节点
index = 5
# 要么遍历到链表结尾,链表长度不够,结束循环,要么找到链表要替换的位置,结束循环
while temp != None and index > 0:
  temp = temp.next
  index -= 1
if temp == None:
  print("fail to replace")
else:
  temp.data = target
在任意索引位置插入节点
# 两种情况,要么索引大于链表长度,那么插在最后,要么索引小于链表,插入索引位置
if head is None or index < 0:
  head= Node(new_data, head)
else:
  temp = head
  # index > 1是为了放置新节点在(index-1)到index之间
  if temp.next != None and index > 1:
    temp = temp.next
    index -= 1
  temp.next = Node(new_data, temp.next)
在任意位置删除节点
# 这里只考虑索引小于链表长度
temp = head
index = 5
while temp.next.next != None and index > 1:
  temp = temp.next
  index -= 1
temp.next = temp.next.next

时间复杂度

# O(1)
从开始处插入节点/从开始处删除节点
# O(n)
在第i个位置访问/替换(平均情况),在第i个位置插入/删除(平均情况)

列表接口

add(data)               # 添加数据项
insert(index, data)     # 插入数据项到索引位置
replace(index, data)    # 将索引位置的数据项替换
pop(index)              # 将索引位置的数据项弹出列表
remove(data)            # 删除某个数据项

栈-STACK

特点

生活中的例子(加深理解)

经典应用

部分应用代码实现

# 我们这里简单的用python内置列表作为基础结构实现栈结构
class Stack:
    """利用列表(底层为数组)构建栈的抽象结构"""
    def __init__(self):
        self._items = []

    def size(self):
        return len(self._items)

    def push(self, item):
        self._items.append(item)

    def pop(self):
        return self._items.pop()

    def is_empty(self):
        return len(self._items) == 0

    def peek(self):
        return self._items[len(self._items) - 1]
#######################################################################

# 中缀转后缀表达式
def infix_to_suffix(expressions):
    """当前约定中缀表达式是以空格进行分割, exp: a + b * c"""
    s = Stack()
    exp_list = expressions.split(" ")
    # 存放操作数
    num_list = []
    # 自定义优先级,"("的存在是为了有多重括号存在时,保证所有的+-*/都可以顺利入栈
    priority_dic = {"(": 1, "+": 2, "-": 2, "*": 3, "/": 3}
    for token in exp_list:
        if token not in "+-*/()":
            num_list.append(int(token))
        elif token == "(":
            s.push(token)
        elif token == ")":
            while s.peek() != "(":
                num_list.append(s.pop())
            # 将"("弹出
            s.pop()
        else:
            while s.size() > 0 and priority_dic.get(token) <= priority_dic.get(s.peek()):
                num_list.append(s.pop())
            s.push(token)
    # 检查栈中是否还有操作符存在,有的话,依依弹出即可
    while not s.is_empty():
        num_list.append(s.pop())
    return "".join([str(i) for i in num_list])


# 计算后缀
def calc_suffix(expression):
    s = Stack()
    for token in expression:
        if token in "0123456789":
            s.push(int(token))
        else:
            # 要考虑到减法和除法顺序不能颠倒的问题
            num_first = s.pop()
            num_second = s.pop()
            tmp_result = convert_to_math(token, num_first, num_second)
            s.push(tmp_result)
    return s.pop()


# 辅助函数,每两个弹出元素的计算结果
def convert_to_math(token, num1, num2):
    if token == "*":
        return num1 * num2
    elif token == "/":
        return num2 / num1
    elif token == "-":
        return num2 - num1
    elif token == "+":
        return num1 + num2

# 测试数据
convert_ret = infix_to_suffix("3 + 5 * 2 - 5 * ( 3 + 2 )")
calcu_ret = calc_suffix(convert_ret)
print(calcu_ret)
####################################################################

# 10进制转换为2/8/16进制的字符串格式
def to_str(n, base):
    :param n: 十进制数
    :param base: 想要转换的进制
    :return: 
    if n < base:
        return str(n)
    elif 2 <= base <= 10:
        return to_str(n // base, base) + str((n % base))
    else:
        convert_string = "0123456789abcdef"
        return to_str(n // base, base) + convert_string[(n % base)]

ret = to_str(541, 2)
print(ret)

队列-QUEUE

特点

生活中的例子(加深理解)

经典应用

部分应用代码实现

约瑟夫/热土豆理论

# 热土豆问题:几个小朋友,围成一圈,用一个热土豆(或别的什么东西),数一个数就抛给下一个人,每数到3,手上有土豆的人就站出来,然后继续,问哪个位置的人最后剩下?

# 利用内置数据类型列表构建队列数据结构
class Queue():
    def __init__(self):
        self._items = []

    def size(self):
        return len(self._items)

    def enqueue(self, data):
        self._items.insert(0, data)

    def dequeue(self):
        return self._items.pop()

    def is_empty(self):
        return len(self._items) == 0

# 热土豆问题逻辑代码
def hot_potato(lists, num):
    q = Queue()
    # 按人名将数据项入队
    for person in lists:
        q.enqueue(person)
    # 直到只剩下一个人,游戏结束
    while q.size() > 1:
        for i in range(num):
            q.enqueue(q.dequeue())
        q.dequeue()
    # 将最后剩下的人返回
    return q.dequeue()

# 模拟热土豆的实例,参数分表代表参加游戏的人名以及土豆每传递几次就出队一个人
last_person = hot_potato(["A", "B", "C", "D", "E", "F", "G", "H", "I", "L", "M", "N", "O"], 3)
print(last_person)

生产者消费者模型

import threading
import queue
 
 
def producer():
    """
    模拟生产者
    :return:
    """
    for i in range(10):
        print("生产包子%s" %i)
        q.put("包子 %s" % i)
 
    print("开始等待所有的包子被取走...")
    q.join()  # 等待这个包子队列被消费完毕
    print("所有的包子被取完了...")
 
 
def consumer(n):
    """
    模拟消费者
    :return:
    """
    while q.qsize() > 0:
        print("%s 取到" % n, q.get())
        q.task_done()  # 每取一个包子,便告知队列这个任务执行完了
 
q = queue.Queue()
 
p = threading.Thread(target=producer,)
p.start()
 
c1 = consumer("xiaochen")
上一篇下一篇

猜你喜欢

热点阅读