数据结构与算法(入门笔记)
数据结构与算法
算法是一种解决问题的思想和方法
五大特性:
1:输入:0个或多个输入。
2:输出:1个或多个输出。
3:有穷性:一定的步骤和时间内完成
4:可行性:每一步都可以执行
5:确定性:每一步不能产生歧义
时间复杂度
T(n) = O(n^x)
最优时间复杂度:算法完成工作最少需要的基本操作
最坏时间复杂度:算法完成工作最多需要的基本操作
平均时间复杂度:算法完成工作平均需要的基本操作
时间复杂度的基本计算规则
1、基本操作:只有常数项,认为其时间复杂度为O(1)
2、顺序结构:加法
3、循环结构:乘法
4、分支结构:取最大值
5、判断一个算法的效率时,一般只考虑操作数量的最高次项,其他次要项可以忽略
6、没有特殊说明时,时间复杂度一般是指最坏时间复杂度
空间复杂度S(n)
空间复杂度是指一个算法运行时所占用存储空间的大小量度
顺序表
顺序表:将元素顺序地存放在一块连续的存储区里,元素间的顺序关系 由它们的存储顺序自然表示。
扩充的两种策略
线性扩充:每次扩充增加固定数目的存储位置,如每次扩充增加10个元素位置,这 种策略可称为线性增长。
特点:节省空间,但是扩充操作频繁,操作次数多。
加倍扩充:每次扩充容量加倍,如每次扩充增加一倍存储空间。
特点:减少了扩充操作的执行次数,但可能会浪费空间资源。以空间换时间,推荐的方式。
list的基本实现技术
list就是一种采用分离式技术实现的动态顺序表
链表
链表:将元素存放在通过链接构造起来的一系列存储块中。
单向链表
单向链表:一个信息域(元素域)和一个链接域。信息域记录本身元素信息,链接域指向下一个链表的信息域。
单向链表的操作:
is_empty() #判断链表是否为空
length() #返回链表的长度
travel() #遍历
add(item) #在头部添加一个节点
append(item) #在尾部添加一个节点
insert(pos, item) #在指定位置pos添加节点
remove(item) #删除一个节点
search(item) #查找节点是否存在
双向链表
双向链表:每个节点有两个链接:一个指 向前一个节点,当此节点为第一个节点时,指向空值;而另一个指向下一个 节点,当此节点为最后一个节点时,指向空值。
双向链表的操作:
is_empty() #判断链表是否为空
length() #返回链表的长度
travel() #遍历
add(item) #在头部添加一个节点
append(item) #在尾部添加一个节点
insert(pos, item) #在指定位置pos添加节点
remove(item) #删除一个节点
search(item) #查找节点是否存在
单向循环链表
单向循环链表:链表中最后一个节点的next域不再为 None,而是指向链表的头节点。
循环链表的操作:
is_empty() #判断链表是否为空
length() #返回链表的长度
travel() #遍历
add(item) #在头部添加一个节点
append(item) #在尾部添加一个节点
insert(pos, item) #在指定位置pos添加节点
remove(item) #删除一个节点
search(item) #查找节点是否存在
栈(stack)
栈(stack),有些地也称为堆栈,是⼀种容器,可存储数据元素、访问元 素、删除元素,它的特点在于只能允许在容器的一端(称为栈顶端指标,英 语:top)进行加减数据(英语:push)和输出数据(英语:pop)的运算。
栈的操作
Stack() #创建一个新的空栈
push(item) #添加一个新的元素item到栈顶
pop() #弹出栈顶元素
peek() #返回栈顶元素
is_empty() #判断栈是否为空
size() #返回栈的元素个数
队列
队列(queue)是只允许在一端进行插入操作,在另一端进行删除操作的 线性表。
队列是一种先进先出的(First In First Out)的线性表,简称FIFO
队列的操作
Queue() #创建一个空的队列
enqueue(item) #往队列中添加一个item元素
dequeue() #从队列头部删除一个元素
is_empty() #判断一个队列是否为空
size() #返回队列的大小
排序与搜索
稳定性
稳定排序算法会让原本有相等键值的纪录维持相对次序。也就是如果一个排序算法是稳定的,当有两个相等键值的纪录R和S,且在原本的列表 中R出现在S之前,在排序过的列表中R也将会是在S之前
冒泡排序
冒泡排序原理
- 相邻替换:相邻的元素进行替换
- 比较循环:重复相邻替换
- 冒泡循环:循环n-1次,最后一个元素不需要进行比较
冒泡排序(稳定)
# 降序
def bubble_sort(alist):
n = len(alist)
for j in range(n-1): #重复交换的次数
# j=[0,1,2···n-1]
for i in range(0,n-1-j): #进行位置交换
if alist[i] > alist[i+1]:
alist[i],alist[i+1] = alist[i+1],alist[i]
选择排序(不稳定)
# 升序
def select_sort(alist):
n = len(alist)
for i in range(n-1): # 确定最小值该在的位置
min_index = i
for j in range(i+1,n): # 确定剩下数组最小值的索引
if alist[j] < alist[min_index]:
min_index = j
if min_index != i: # 若最小值不在最小索引处,交换位置
alist[i],alist[min_index] = alist[min_index],alist[i]
插入排序(稳定)
def insert_sort(alist):
'''插入排序'''
n = len(alist)
for i in range(1,n):
for j in range(i,1,-1):
if alist[j] < alist[j-1]:
alist[j-1],alist[j] = alist[j],alist[j-1]
else:
break
希尔排序(不稳定)
def shell_sort(alist):
n = len(alist)
# 初始步长
gap = n // 2
while gap > 0:
for i in range(gap,n):
j = i
while j >= gap and alist[j-gap] > alist[j]:
alist[j-gap],alist[j] = alist[j],alist[j-gap]
j -= gap
gap = gap//2
快速排序(不稳定)
def quick_sort(alist):
'''快速排序'''
n = len(alist)
if start >= end:
return
mid = alist[start]
left = start
right = end
# left与right未重合,向中间移动
while left < right:
while left < right and alist[right] >= mid:
right -= 1
alist[left] = alist[right]
while left < right and alist[left]< mid:
left += 1
alist[right] = alist[left]
# 从循环退出后,left与right相遇,即left=right
alist[left] = mid
# 对左边部分进行快速排序
quick_sort(alist,start,left-1)
# 对右边进行快速排序
quick_sort(alist,left+1,end)
归并排序(稳定)
n个元素
二分法分割m次后的元素数n/2^m=1,m=logn
合并操作,执行n次
def merge_sort(alist):
n = len(alist)
if 1 == n :
return alist
mid = n//2
left_sorted_li = merge_sort(alist[:mid])
right_sorted_li = merge_sort(alist[mid:])
left,right = 0,0
merge_sorted_li = []
left_n = len(left_sorted_li)
right_n = len(right_sorted_li)
while left < left_n and right < right_n:
if left_sorted_li[left] <= right_sorted_li[right]:
merge_sorted_li.append(left_sorted_li[left])
left += 1
else:
merge_sorted_li.append(right_sorted_li[right])
right += 1
merge_sorted_li += left_sorted_li[left:]
merge_sorted_li += right_sorted_li[right:]
return merge_sorted_li
树
二叉树
class Node(object):
def __init__(self,item):
self.item = item
self.lchild = None
self.rchild = None
class BinaryTree(object):
'''二叉树'''
def __init__(self,node=None):
selt.root = node
def add(self,item):
if self.root is None:
self.root = Node(item)
else:
queue = []
queue.append(self.root)
while len(queue) > 0:
node = queue.pop(0)
if not node.lchild:
node.lchild = Node(item)
return
else:
queue.append(node.lchild)
if not node.rchild:
node.rchild = Node(item)
return
else:
queue.append(node.rchild)
def bread_travel(self):
'''广度优先遍历'''
if self.root is None:
return
queue = []
queue.append(self.root)
while len(queue)>0:
node = queue.pop(0)
if node.lchild:
queue.append(node.lchild)
if not rchild:
queue.append(node.rchild)
def preorder_travel(self,root):
'''先序遍历'''
if root:
self.preorder_travel(root.lchild)
self.preorder_travel(root.rchild)
def inorder_travel(self,root):
'''中序遍历'''
if root:
self.inorder_travel(root.lchild)
self.inorder_travel(root.rchild)
def postorder_travel(self,root):
'''后序遍历'''
if root:
self.postorder_travel(root.lchild)
self.postorder_travel(root.rchild)