数据结构说明及代码合集
算法:输入 输出 有穷性 确定性 可行性
顺序表:表头信息(容量,个数) 数据区
一体式 分离式
1 栈:栈(有时称为“后进先出栈”)是一个元素的有序集合,其中添加移除新元素总发生在同一端。
class Stack:
def __init__(self):
self.items = []
def isEmpty(self):
return self.items == []
def push(self, item):
self.items.append(item)
def pop(self):
return self.items.pop()
def peek(self):
return self.items[len(self.items)-1]
def size(self):
return len(self.items)
2 队列:队列是项的有序结合,其中添加新项的一端称为队尾,移除项的一端称为队首
class Queue:
def __init__(self):
self.items = []
def isEmpty(self):
return self.items == []
def enqueue(self, item):
self.items.insert(0,item)
def dequeue(self):
return self.items.pop()
def size(self):
return len(self.items)
面试题:如何实现两个队列生成一个栈
q1 = Queue()
q2 = Queue()
alist = [1,2,3,4,5]
#将数据加入队列
for i in alist:
q1.enqueue(i)
while True:
while q1.size() > 1:
item = q1.dequeue()
q2.enqueue(item)
print(q1.dequeue())
q1,q2 = q2,q1
if q1.size() == 0:
break
队列应用:烫手山芋游戏介绍:6个孩子围城一个圈,排列顺序孩子们自己指定。第一个孩子手里有一个烫手的山芋,需要在计时器计时1秒后将山芋传递给下一个孩子,依次类推。规则是,在计时器每计时7秒时,手里有山芋的孩子退出游戏。该游戏直到剩下一个孩子时结束,最后剩下的孩子获胜。请使用队列实现该游戏策略,排在第几个位置最终会获胜。
分析:为了模拟这个圈,我们可以使用队列。假设游戏开始时,排在队列中的第一个(队首)的孩子手里拿着山芋。游戏开始后,拿着山芋的孩子出队列然后再入队列,将山芋传递给下一个孩子。每当山芋到队首孩子手里后,队首的孩子先出队列再入队 列,一次类推。当传递六次后,手里有山芋的孩子淘汰,游戏继续,继续传递山芋。
from basic.queue import Queue
def hotPotato(namelist, num):
simqueue = Queue()
for name in namelist:
simqueue.enqueue(name)
while simqueue.size() > 1:
for i in range(num):
kid = simqueue.dequeue()
simqueue.enqueue(kid)
simqueue.dequeue()
return simqueue.dequeue()
3 双端队列:概念:deque(也称为双端队列)是与队列类似的项的有序集合。它有两个端部,首部和尾部,并且项在集合中保持不变。
class Deque:
def __init__(self):
self.items = []
def isEmpty(self):
return self.items == []
def addFront(self, item):
self.items.append(item)
def addRear(self, item):
self.items.insert(0,item)
def removeFront(self):
return self.items.pop()
def removeRear(self):
return self.items.pop(0)
def size(self):
return len(self.items)
双端队列应用- 回文检测:设计程序,检测一个字符串是否为回文
分析:该问题的解决方案将使用 deque 来存储字符串的字符。我们从左到右处理字符串,并将每个字符添加到 deque 的尾部。在这一点上,deque 像一个普通的队列。然而,我们现在可以利用 deque 的双重功能。 deque 的首部保存字符串的第一个字符,deque 的尾部保存最后一个字符。我们可以直接删除并比较首尾字符,只有当它们匹配时才继续。如果可以持续匹配首尾字符,我们最终要么用完字符,要么留出大小为 1 的deque,取决于原始字符串的长度是偶数还是奇数。在任一情况下,字符串都是回文。
from basic.deque import Deque
def palchecker(aString):
chardeque = Deque()
for ch in aString:
chardeque.addRear(ch)
stillEqual = True
while chardeque.size() > 1 and stillEqual:
first = chardeque.removeFront()
last = chardeque.removeRear()
if first != last:
stillEqual = False
return stillEqual
print(palchecker("lsdkjfskf"))
print(palchecker("radar"))
内存空间也会有这两个最基本的属性:内存空间大小和内存空间的地址。内存空间的大小可以表示该空间可以存储数据值的大小范围,内存空间的地址(用十六进制数值表示)可以用来通过寻址定位、查找到该内存空间中所存储的数据值。
引用:当一个变量中存储的是某一块内存空间的地址,则该变量即可成为那块内存空间的引用。a=10,a就是10所在内存空间的一个引用。
指向:当一个变量中存储了一块内存空间的地址,则称该变量(引用)指向了那块内存。
不同类型数据占用内存空间的大小:整形(4字节),浮点型(8字节),字符型(1字节)
顺序表:list 和 tuple 弊端:顺序表的结构需要预先知道数据大小来申请连续的存储空间,而在进行扩充时又需要进行数据的搬迁。
4 链表(Linked list)是一种常见的基础数据结构,是一种线性表,但是不像顺序表一样连续存储数据,而是每一个结点(数据存储单元)里存放下一个结点的信息(即地址)
# coding=utf-8
# 单向链表
class Node():
def __init__(self,item):
self.item = item
self.next = None
def __str__(self):
return str(self.item)
class Link():
def __init__(self):
#永远指向链表中第一个节点
self._head = None
def isEmpty(self):
return self._head is None
def add(self,item):
node = Node(item)
node.next = self._head
self._head = node
def length(self):
count = 0
if self.isEmpty():
return count
else:
cur = self._head
while cur is not None:
count += 1
cur = cur.next
return count
def travel(self):
cur = self._head
while cur is not None:
print(cur)
cur = cur.next
def append(self,item):
node = Node(item)
cur = self._head
if self.isEmpty():
self._head = node
else:
while cur is not None:
#因为循环遍历结束后cur会指向空并非最后一个节点
pre_cur = cur
cur = cur.next
pre_cur.next = node
def search(self,item):
ex = False
cur = self._head
while cur is not None:
if cur.item == item:
ex = True
break
cur = cur.next
return ex
def insertTo(self,item,index):
cur = self._head
ex = 0
node = Node(item)
#插入到第一个节点位置
if index <= 0:
self.add(item)
#插入到最后一个节点位置
elif index >= self.length():
self.append(item)
else:
while cur is not None:
pre = cur
cur = cur.next
#此处插入的一定不是第一个节点和最后一个节点位置,因此index要减1
if ex == index-1:
node.next = cur
pre.next = node
break
ex += 1
def remove(self,item):
pre = None
cur = self._head
#删除的是第一个节点
if cur.item == item:
self._head = cur.next
else:
while cur is not None:
pre = cur
cur = cur.next
if cur.item == item:
pre.next = cur.next
cur.next = None
cur = cur.next
单向循环链表
# coding=utf-8
class Node:
"""节点"""
def __init__(self, item):
self.item = item
self.next = None
def __str__(self):
return str(self.item)
class SinCycLinkedList:
"""单向循环链表"""
def __init__(self):
self._head = None
def is_empty(self):
"""判断链表是否为空"""
return self._head is None
def length(self):
"""链表长度"""
if self.is_empty():
return 0
count = 1
cur = self._head
while cur.next != self._head:
# print("cur", cur.item)
count += 1
cur = cur.next
return count
def travel(self):
"""遍历"""
if self.is_empty():
return
cur = self._head
print(cur.item)
while cur.next != self._head:
cur = cur.next
print(cur.item)
def add(self, item):
"""在头部添加一个节点"""
node = Node(item)
if self.is_empty():
self._head = node
node.next = self._head
else:
node.next = self._head
cur = self._head
while cur.next != self._head:
cur = cur.next
cur.next = node
self._head = node
def append(self, item):
"""在尾部添加一个节点"""
node = Node(item)
if self.is_empty():
self._head = node
node.next = self._head
else:
cur = self._head
# print(type(cur), cur.item, cur.next)
while cur.next != self._head:
cur = cur.next
# print(cur.item)
cur.next = node
node.next = self._head
def insert(self, pos, item):
"""指定位置pos添加节点"""
if pos <= 0:
self.add(item)
elif pos > (self.length() - 1):
self.append(item)
else:
node = Node(item)
cur = self._head
cur_pos = 0
while cur.next != self._head:
if (pos - 1) == cur_pos:
node.next = cur.next
cur.next = node
break
cur_pos += 1
cur = cur.next
def remove(self, item):
"""删除一个节点"""
if self.is_empty():
return
pre = self._head
# 删除首节点
if pre.item == item:
cur = pre
while cur.next != self._head:
cur = cur.next
cur.next = pre.next # 删除首节点(跳过该节点)
self._head = pre.next # 重新指定首节点
# 删除其他的节点
else:
cur = pre
while cur.next != self._head:
if cur.next.item == item:
cur.next = cur.next.next
cur = cur.next
def search(self, item):
"""查找节点是否存在"""
if self.is_empty():
return -1
cur_pos = 0
cur = self._head
if cur.item == item:
return cur_pos
while cur.next != self._head:
if cur.item == item:
return cur_pos
cur_pos += 1
cur = cur.next
if cur_pos == self.length() - 1:
return -1
coding=utf-8
双向链表
class Node:
"""节点"""
def __init__(self, item):
self.item = item
self.prev = None
self.next = None
class DLinkList:
"""双向链表"""
def __init__(self):
self._head = None
def is_empty(self):
"""判断链表是否为空"""
return self._head is None
def length(self):
"""获取链表长度"""
if self.is_empty():
return 0
else:
cur = self._head
count = 1
while cur.next is not None:
count += 1
cur = cur.next
return count
def travel(self):
"""遍历链表"""
print("↓↓" * 10)
if self.is_empty():
print("")
else:
cur = self._head
print(cur.item)
while cur.next is not None:
cur = cur.next
print(cur.item)
print("↑↑" * 10)
def add(self, item):
"""链表头部添加节点"""
node = Node(item)
if self.is_empty():
self._head = node
else:
cur = self._head
node.next = cur
cur.prev = node
self._head = node
def append(self, item):
"""链表尾部添加节点"""
node = Node(item)
if self.is_empty():
self._head = node
else:
cur = self._head
# 遍历找到最后一个节点
while cur.next is not None:
cur = cur.next
# 在尾节点添加新的节点
cur.next = node
node.prev = cur
def insert(self, pos, item):
"""指定位置添加"""
# 头部添加
if pos <= 0:
self.add(item)
# 尾部添加
elif pos > (self.length() - 1):
self.append(item)
# 其他位置添加
else:
node = Node(item)
cur = self._head
cur_pos = 0
while cur.next is not None:
if cur_pos == (pos - 1):
# 与下一个节点互相指向
node.next = cur.next
cur.next.prev = node
# 与上一个节点互相指向
cur.next = node
node.prev = cur
cur_pos += 1
cur = cur.next
def remove(self, item):
"""删除节点"""
if self.is_empty():
return
else:
cur = self._head
# 删除首节点
if cur.item == item:
self._head = cur.next
cur.next.prev = None
# 删除其他节点
else:
while cur.next is not None:
if cur.item == item:
# 删除之前:1 ←→ [2] ←→ 3
# 删除之后:1 ←→ 3
cur.prev.next = cur.next
cur.next.prev = cur.prev
cur = cur.next
# 删除尾节点
if cur.item == item:
cur.prev.next = None
def search(self, item):
"""查找节点是否存在"""
if self.is_empty():
return -1
else:
cur = self._head
cur_pos = 0
while cur.next is not None:
if cur.item == item:
return cur_pos
cur_pos += 1
cur = cur.next
if cur_pos == (self.length() - 1):
return -1
面试题:如何将单链表倒序
def reverse(self):
if self._head:
cur = self._head
pre = None
cur_next = cur.next
if cur.next is None:
return
while True:
cur.next = pre
pre = cur
cur = cur_next
if cur == None:
self._head = cur
break
cur_next = cur_next.next
self._head = pre
5顺序查找/搜索
搜索查找(从头到尾)
def sequentialSearch(alist, item):
pos = 0
found = False
while pos < len(alist) and not found:
if alist[pos] == item:
found = True
else:
pos = pos+1
return found
testlist = [1, 2, 32, 8, 17, 19, 42, 13, 0]
print(sequentialSearch(testlist, 3))
print(sequentialSearch(testlist, 13))
排序查找(没有就直接强退)
def orderedSequentialSearch(alist, item):
pos = 0
found = False
stop = False
while pos < len(alist) and not found and not stop:
if alist[pos] == item:
found = True
else:
if alist[pos] > item:
stop = True
else:
pos = pos+1
return found
testlist = [0, 1, 2, 8, 13, 17, 19, 32, 42,]
print(orderedSequentialSearch(testlist, 3))
print(orderedSequentialSearch(testlist, 13))
二分查找
def binarySearch(alist, item):
first = 0
last = len(alist)-1
found = False
while first<=last and not found:
midpoint = (first + last)//2
if alist[midpoint] == item:
found = True
else:
if item < alist[midpoint]:
last = midpoint-1
else:
first = midpoint+1
return found
testlist = [0, 1, 2, 8, 13, 17, 19, 32, 42,]
print(binarySearch(testlist, 3))
print(binarySearch(testlist, 13))
哈希表:是一种非常容易且便捷就可以定位到某一个具体元素的集合。哈希表的每个位置,通常称为一个槽,每个槽可以容纳一个元素,并且由从 0 开始的整数值命名。
hash函数:元素和元素在hash表中所属的槽之间的映射被称为hash函数
哈希值(元素放的对应位置):使用余除法计算哈希值 h(item) = item%11
当我们要搜索一个元素时,我们只需使用哈希函数来计算该元素的槽名称,然后检查哈希表以查看它是否存在。要求:每个元素映射到哈希表中的位置是唯一的
6排序
1 冒泡排序
#核心:将乱序列表中最大元素排列到列表末尾位置
def m_sort(alist):
for j in range(0,len(alist)-1):
if alist[j] > alist[j+1]:
temp = alist[j]
alist[j] = alist[j+1]
alist[j+1] = temp
return alist
def m_sort_final(alist):
for i in range(0,len(alist)-1):
for j in range(0,len(alist)-i-1):
if alist[j] > alist[j+1]:
temp = alist[j]
alist[j] = alist[j+1]
alist[j+1] = temp
return alist
2 选择排序(改进冒泡,每次只做一次交换)
def selectionSort(alist):
for i in range(len(alist)-1,0,-1):
positionOfMax=0
for location in range(1,i+1):
if alist[location]>alist[positionOfMax]:
positionOfMax = location
temp = alist[i]
alist[i] = alist[positionOfMax]
alist[positionOfMax] = temp
alist = [54,26,93,17,77,31,44,55,20]
selectionSort(alist)
print(alist)
3 插入排序(gap为1的希尔排序)
主要思想是每次取一个列表元素与列表中已经排序好的列表段进行比较,然后插入从而得到新的排序好的列表段,最终获得排序好的列表。
step_1:
def sort(alist):
i = 1
if alist[i] < alist[i-1]:
alist[i],alist[i-1] = alist[i-1],alist[i]
step_2:
def sort(alist):
i = 2
while i > 0:
if alist[i] < alist[i-1]:
alist[i],alist[i-1] = alist[i-1],alist[i]
i -= 1
完整代码:
def sort(alist):
for i in range(1,len(alist)):
while i > 0:
if alist[i] < alist[i-1]:
alist[i],alist[i-1] = alist[i-1],alist[i]
i -= 1
else:
break
return alist
4 希尔排序
该方法的基本思想是:先将整个待排元素序列分割成若干个子序列(由相隔某个“增量(gap)”的元素组成的)分别进行直接插入排序,然后依次缩减增量再进行排序,待整个序列中的元素基本有序(增量足够小)时,再对全体元素进行一次直接插入排序。因为直接插入排序在元素基本有序的情况下(接近最好情况),效率是很高的,因此希尔排序在时间效率比直接插入排序有较大提高。
step_1:
def sort(alist):
gap = len(alist) // 2
#将插入排序当做增量为1的希尔排序
for i range(1,len(alist)):
while i > 0 :
if alist[i] < alist[i-1]:
alist[i],alist[i-1] = alist[i-1],alist[i]
i -= 1
else:
break
- step_2:
def sort(alist):
gap = len(alist) // 2
#将增量设置成gap
for i range(gap,len(alist)):
while i > 0 :
if alist[i] < alist[i-gap]:
alist[i],alist[i-gap] = alist[i-gap],alist[i]
i -= gap
else:
break
- 完整代码:
#继续缩小增量
def sort(alist):
gap = len(alist) // 2
while gap >= 1:
#将增量设置成gap
for i in range(gap,len(alist)):
while i > 0 :
if alist[i] < alist[i-gap]:
alist[i],alist[i-gap] = alist[i-gap],alist[i]
i -= gap
else:
break
gap //= 2
return alist
5 归并排序
6 快速排序
• 将列表中第一个元素设定为基准数字,赋值给mid变量,然后将整个列表中比基准小的数值放在基准的左侧,比基准到的数字放在基准右侧。然后将基准数字左右两侧的序列在根据此方法进行排放。
• 定义两个指针,low指向最左侧,high指向最右侧
• 然后对最右侧指针进行向左移动,移动法则是,如果指针指向的数值比基准小,则将指针指向的数字移动到基准数字原始的位置,否则继续移动指针。
• 如果最右侧指针指向的数值移动到基准位置时,开始移动最左侧指针,将其向右移动,如果该指针指向的数值大于基准则将该数值移动到最右侧指针指向的位置,然后停止移动。
• 如果左右侧指针重复则,将基准放入左右指针重复的位置,则基准左侧为比其小的数值,右侧为比其大的数值。
- 第一次排序:将将比基准小的排列在基准左侧,比基准大的排列在基准右侧
def sort(alist):
low = 0
high = len(alist)-1
#基准:最左侧的数值
mid = alist[low]
#low和high的关系只能是小于,当等于的时候就要填充mid了
while low < high:
while low < high:
if alist[high] > mid:
high -= 1
else:
alist[low] = alist[high]
break
while low < high:
if alist[low] < mid:
low += 1
else:
alist[high] = alist[low]
break
#当low和high重复的时候,将mid填充
if low == high:
alist[low] = mid #or alist[high] = mid
break
return alist
- 完整代码:
def sort(alist,start,end):
low = start
high = end
#递归结束的条件
if low > high:
return
#基准:最左侧的数值
mid = alist[low]
#low和high的关系只能是小于,当等于的时候就要填充mid了
while low < high:
while low < high:
if alist[high] > mid:
high -= 1
else:
alist[low] = alist[high]
break
while low < high:
if alist[low] < mid:
low += 1
else:
alist[high] = alist[low]
break
#当low和high重复的时候,将mid填充
if low == high:
alist[low] = mid #or alist[high] = mid
break
#执行左侧序列
sort(alist,start,high-1)
#执行右侧序列
sort(alist,low+1,end)
return alist