数据结构与算法 20210416

引入

算法简介

  • 算法:独立存在的一种解决问题的思想和方法,是计算机处理信息的本质
    -五大特性:输入、输出、有穷形、确定性、可行性

算法效率衡量

  • 实现算法程序的执行时间可以反映出算法的效率,即算法的优劣。
  • 单纯依靠运行时间比较算法的优劣不准确,因为客观原因(硬件和操作系统)会影响程序的执行时间
  • 大O记法:对于单调的整数函数f,如果存在一个整数函数g和实常数c>0,使得对于充分的n总有f(n)<=c*g(n),即函数g是f的一个渐进函数(忽略常数),记为f(n)=O(g(n))。即在趋向无穷的极限意义下,函数f的增长函数受函数g的约束,即函数f与函数g的特征相似。
  • 时间复杂度:假设存在函数g,使得算法A处理规模n的问题示例所用时间为T(n)=O(g(n)),则称O(g(n))为算法A的渐进时间复杂度,简称时间复杂度,记为T(n)
  • 时间复杂度分为:最坏时间复杂度<主要关注>、最好时间复杂度、平均时间复杂度
  • 时间复杂度基本计算规则:
    -基本操作,即只有常数项,认为其复杂度为O(1)
    -顺序结构,时间复杂度按加法进行计算
    -循环结构,时间复杂度按乘法进行计算
    -分支结构,时间复杂度取最大值
    -判断一个算法的效率时,往往只需关注操作数量的最高次项,其他次要项和常数项可以忽略
    -没有特别的说明是,分析的时间复杂度均是最坏时间复杂度
  • 常见时间复杂度关系:
    O(1)<O(logn)<O(n)<O(nlogn)<O(n2)<O(n3)<O(2n)<O(n!)<O(nn)

python内置类型性能分析

  • timeit模块:class timeit.Timer(stmt=‘pass’, setup=“pass”, timer= < timerfunction >)
    -Timer:测量小段代码执行素的的类
    -stmt:测量的代码语句(statement)
    -setup:运行代码需要的设置
    -timer:定时器函数,与平台有关
    timeit.Timer.timeit(number=10000) #number测试代码时的测试次数,方法返回执行代码的平均耗时。
def test():
	l=[i for i inrange(1000)]
from timeit import Timer
t=Timer("test()","from __main__ import test")
print(t1.timeit(number=1000),'seconds')
  • list内置操作的时间复杂度
    indexx:O(1)
    index assignment:O(1)
    append:O(1)
    pop():O(1)
    pop(i):O(n)
    insert(i,item):O(n)
    del operator:O(n)
    iteration:O(n)
    contains(in):O(n)
    get slice[x:y]:O(k)
    del slice:O(n)
    set slice:O(n+k)
    reverse:O(n)
    concatenate:O(k)
    multiply:O(nk)
  • dict内置操作的时间复杂度
    copy:O(n)
    get item:O(1)
    set item:O(1)
    delete item:O(1)
    contains item:O(1)
    iteration:O(n)

数据结构

  • 数据结构:数据对象中数据元素之间的关系。分为内置数据结构<列表、元组、字典>与扩展数据结构<栈、队列>
  • 数据结构仅描述数据元素之间的关系,程序=数据结构+算法。算法是为了解决实际问题设计的,数据结构是算法需要处理的问题载体。
  • 抽象数据类型:abstratct data type:一个数据模型以及定义在此数据模型上的一组操作,即数据类型和数据类型的运算进行封装。目的:把数据的类型的表示和数据类型上运算的实现与这些数据类型和运算在程序中的引用隔开,使他们独立。
    -最常用的数据运算:插入、删除、修改、查找、排序

顺序表

顺序表的基本形式

顺序表的基本形式

  • 顺序表中数据元素连续存储,每个元素所占用的存储单元大小固定相同,元素的下标是逻辑地址,而元素存储的物理地址(实际内存地址)可以通过存储区的起始地址Loc(e0)加上逻辑地址与存储单元大小的乘积计算而得。
  • b中元素外置的形式,实际将数据元素另行存储,顺序表中各单元位置保存对应元素的地址信息。

顺序表的结构与实现

  • 顺序表的结构:
    1)元素的集合<数据区>
    2)实现正确操作而需记录的信息,即表的整体情况的信息,包括元素储存区的容量和当前已有的元素个数<表头信息>
  • 顺序表的两种基本实现形式
    1)一体式结构:存储表信息的单元与元素存储区已连续的方式安排在一块存储区里,两部分数据整体形成一个完整的顺序列表。<整体性强,便于管理,但当创建后元素存储区固定>
    2)分离式结构:表对象只保存整个表有关的信息(容量和元素个数),实际数据元素存放在另一个独立的元素存储区,通过链接与基本表对象关联。
  • 元素存储区扩容
    -一体式结构,若更新数据区,只能整体迁移,即整个顺序表对象改变了
    -分离式结构:只需将表信息区中的数据区链接地址更新即可,而该顺序表对象不改变
  • 元素存储区扩容:
    -分离式结构的顺序表的容量可以在使用中动态变化,采用这种技术实现的顺序表称为动态顺序表。扩容的两种策略
    1)每次扩容中增加固定数目的存储位置,即线性增长。<以时间换空间>
    2)每次扩容量加倍<以空间换时间>

顺序表的操作

  • 增加元素:
    1)表尾末端加入元素:时间复杂度为O(1)
    2)非保序的元素插入:插入时间复杂度为O(1)
    3)保序的元素插入:时间复杂度为O(n)
    其中保序指逻辑地址按顺序排列,非保序的元素插入指将原位置的数据替换,将其放置在列表末尾
  • 删除元素
    1)删除表尾元素:时间复杂度为O(1)
    2)非保序的元素删除:插入时间复杂度为O(1)
    3)保序的元素删除:时间复杂度为O(n)

python中的顺序表

  • python中的list和tuple两种类型采用了顺序表的实现技术。
  • list的基本实现技术:分离式技术实现的动态顺序表,list实现采用在建立空表时,系统分配一块能够容纳8个元素的存储区,在执行插入操作时更换4倍大的存储区;当达到阈值50000,则增加一倍,避免空间浪费。
    -基于下标的高效元素访问和更新,时间复杂度为O(1)。<采用顺序表技术>
    -允许任意加入元素,而且在不断加入元素的过程,表对象的标识id不变<采用分离式实现技术>

链表

  • 链表linked list:一种常见的基础数据结构,是一种线性表,在每一个节点(数据存储单元)里存放下一个节点的位置信息(即地址)

单向链表

  • 单向链表:链表中的最简单的形式,每个节点包含了两个域,信息域(元素域)和链接域。最后一个节点的链接域指向一个空值。
    -元素域elem:用来存放具体的数据
    -链接域next:用来存放下一个节点的位置
    -变量p指向链表的头节点的位置,从p出发能找到表的任意节点
  • 单链表的操作:
    is_empty():链表是否为空
    length():链表的长度
    travel():遍历整个链表
    add(item):链表头部添加元素
    append(item):链表尾部添加元素
    insert(pos,item):指定位置添加元素
    remove(item):删除节点
    search(item):查找节点
#节点的实现
class Node(object):
	"""单链表的结点"""
	def __init__(self,item):
		self.elem=item #存放数据元素
		self.next=None #next是下一个节点的标识

#单链表的实现		
class SingleLinkList(object):
	"""单链表"""
	def __init__(self,node=None):
		self.__head=None
	
	def is_empty(self):
	'''链表是否为空'''
		return self.__head==None
	
	def length(self):
	"""链表的长度"""
		cur=self.__head
		count=0
		if cur!=None:
			count+=1
			cur=self.next
		return count
	
	def travel(self):
	'''遍历整个链表'''
		cur=self.__head
		while cur!=None:
			print(cur.elem,end=" ")
			cur=cur.next
		print ("")
		
	
	def add(self,item):
	'''链表头部添加元素'''
		node=Node(item)
		node.next=self.__head
		self.__head=node
	
	def append(self,item):
	'''链表尾部添加元素'''
		node=Node(item)
		if self.is_empty():
			self.__head=node
		else:
			cur=self.__head
			while cur.next!=None:
				cur=cur.next
			cur.next=node
			
	def insert(pos,item):
	'''指定位置添加元素'''
	if pos<=0:
		self.add(item)
	elif pos>(self.length()-1):
		self.append(item)
	else:
		pre=self.__head
		count=0
		while count<(pos-1):
			count+=1
			pre=pre.next
		node=Node(item)
		node.next=pre.next
		pre.next=node
		
	def remove(self,item):
	'''删除节点'''
		cur=self.__head
		whie cur!=None:
			if cur.elem==item:
				self.__head=cur.next
				if cur.next:
					cur.next.prev=None
			else:
				cur.prev.next=cur.next
				if cur.next:
					cur.next.prev=cur.prev
				break
			else:
				cur=cur.next
				
	def search(self,item):
	'''查找节点'''
		cur=self.__head
		while cur!=None
			if cur.elem==item:
				return Ture
			else:
				cur=cur.next
		return False
  • 链表与顺序表的对比:顺序表和链表的插入和删除是不同的操作,顺序表主要是拷贝覆盖,链表主要是遍历查找

单向循环链表

  • 单向循环链表:即链表的最后一个节点的next不指向None,而是指向链表的头节点
class Node(object):
	"""单链表的结点"""
	def __init__(self,item):
		self.elem=item #存放数据元素
		self.next=None #next是下一个节点的标识

#单链循环表的实现		
class SinglecirculateLinkList(object):
	"""单链表"""
	def __init__(self,node=None):
		if node:
			node.next=node
		self.__head=None
	
	def is_empty(self):
	'''链表是否为空'''
		return self.__head==None
	
	def length(self):
	"""链表的长度"""
		cur=self.__head
		count=1
		if cur!=self.__head:
			count+=1
			cur=self.next
		return count
	
	def travel(self):
	'''遍历整个链表'''
		cur=self.__head
		if self.is_empty():
			return
		while cur.next!=self.__head:
			print(cur.elem,end=" ")
			cur=cur.next
		print(cur.elem,end=" ")
		print ("")
	
	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
			while cur.next!=self.__head:
				cur=cur.next
			cur.next=node
			node.next=self.__head
			
	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
		count=0
		while count<(pos-1):
			count+=1
			cur=cur.next
		node.next=cur.next
		cur.next=node
		
	def remove(self,item):
	'''删除节点'''
		if self.is_empty():
			return
		cur=self.__head
		pre=None:
		if self.elem==item:
			if cur.next!=self.__head:
				while cur.next!=self.__head:
					cur=cur.next
				cur.next=self.__head.next
				self.__head=self.__head.next
			else:
				self.__head=None
		else:
			pre=self.__head
			while cur.next!=self.__head:
				if cur.item==item:
					pre.next=cur.next
					return
				else:
					pre=cur
					cur=cur.next
			if cru.elem==item:
				pre.next=cur.next
			
	def search(self,item):
	'''查找节点'''
		if self.is_empty():
			return False
		cur=self.__head
		if cur.item=item
			return Ture
		
		while cur.next!=self.__head:
			cur=cur.next
			if cur.elem=item
				return Ture
		return False

双向链表

  • 双向链表\双面链表:每个节点有两个链接,一个指向前一个节点,当此节点为第一个节点时,指向空值。另一个指向后一个节点,当此节点为最后一个节点时,指向空值。
  • 双向链表的操作:
    is_empty():链表是否为空
    length():链表的长度
    travel():遍历整个链表
    add(item):链表头部添加元素
    append(item):链表尾部添加元素
    insert(pos,item):指定位置添加元素
    remove(item):删除节点
    search(item):查找节点
#节点的实现		
class Node(object):
'''双向链表的结点'''
	def __init__(self,item):
	self.elem=item
	self.prev=None
	self.next=None
#双向链表的实现		
class DoubleLinkList(object):
	"""双向链表"""
	def __init__(self,node=None):
		self.__head=None
	
	def is_empty(self):
	'''链表是否为空'''
		return self.__head==None
	
	def length(self):
	"""链表的长度"""
		cur=self.__head
		count=0
		if cur!=None:
			count+=1
			cur=self.next
		return count
	
	def travel(self):
	'''遍历整个链表'''
		cur=self.__head
		while cur!=None:
			print(cur.elem,end=" ")
			cur=cur.next
		print ("")
		
 	def add(self,item):
        '''链表头部添加元素'''
        node = Node(item)
        if self.is_empty():
            self.__head=node
        else:
            node.next = self.__head
            self.__head = node
            node.next.prev=node
	
    def append(self,item):
        '''链表尾部添加元素'''
        node = Node(item)
        if self.is_empty():
            self.__head = node
        else:
            cur = self.__head
            while cur.next != None:
                cur = cur.next
            node.prev=cur
            cur.next = node
			
 	def insert(self, index, item):
        node = Node(item)
        cur = self.__head
        count = 0
        if index <= 0:
            self.add(item)
        elif index > (self.length() - 1):
            self.append(item)
        else:
            while count < index:
                count += 1
                cur = cur.next
            node.next = cur
            node.prev = cur.prev
            cur.prev.next = node
            cur.prev = node
            
	 def remove(self,item):
        cur = self.__head
        while cur != None:
            if cur.item == item:
                if self.__head == cur:
                    self.__head = cur.next
                    if cur.next:
                        cur.next.prev = None
                else:
                    cur.prev.next = cur.next
                    if cur.next:
                        cur.next.prev = cur.prev
                    pass
                break
            else:
                cur = cur.next

	
	def search(self,item):
	'''查找节点'''
		cur=self.__head
		while cur!=None
			if cur.elem==item:
				return Ture
			else:
				cur=cur.next
		return False

  • 栈stack:特点是在于只能允许容器的一端(栈顶top)进行加入数据push和输出数据pop的运算。数据结构只允许一端操作,因此后进先出LIFO last in first out的原理运作。

栈结构的实现

  • 栈可以用顺序表实现,也可以用链表实现
  • 栈的操作
    -Stack():创建一个新的元素item到栈顶
    -push(item):添加一个新的元素item到栈顶
    -pop():弹出栈顶元素
    -peek():返回栈顶元素
    -is_empty():判断栈是否为空
    -size():返回栈的元素个数
class Stack(object):
	'''创建一个新的元素item到栈顶'''
	def __init__(self):
		self.__items=[]
	def is_empty():
		'''判断栈是否为空'''
		returnself.items==[]
	def push(self,item):	
		'''添加一个新的元素item到栈顶'''
		self.__items.append(item)
	def pop(self):
		'''弹出栈顶元素'''
		self.__items.pop()
	def peek(self):
		'''返回栈顶元素'''
		if self.__items:
			return self.__items.pop()
		else:
			return None
	def size(self):
		'''返回栈的元素个数'''
		return len(self.__items)

队列

  • 队列queue:只允许在一端插入操作,在另一端进行删除操作的线性表
  • 队列:是一种先进先出的线性表,FIFO。允许插入的一端为队尾,允许删除的一头为对头

单向队列

  • 操作
    -Queue():创建一个空的队列
    -enqueue(item):往队列中添加一个item元素
    -dequeue():从队列头部删除一个元素
    -is_empty():判断队列是否为空
    -size():返回队列的大小
class Queue(object):
	def __init__(self):
		self.__items=[]
	def enqueue(self,item):
		'''往队列中添加一个item元素'''
		self.__items.append(item)
		#self.__items.insert(0,item),取决于是添加与删除操作数更多
	def dequeue(self):
		'''从队列头部删除一个元素'''
		self.__items.pop(0)
		#self.__items.pop(-1)
	def is_empty(self):
		'''判断队列是否为空'''
		return self.__items==[]
	def size(self):
		'''返回队列的大小'''
		return len(self.__items)

双端队列

  • 双端队列deque:double-ended queue:一种具有队列和栈的数据结构。双端队列中的元素可以从两端弹出,其限定插入和删除操作在表的两端进行,双端队列可以在队列任意一端的入队和出队。
  • 操作
    -Deque():创建一个空的双端队列
    -add_front(item):从队头加入一个item元素
    -add_rear(item):从队尾加入一个item元素
    -remove_front():从队头删除一个item元素
    -remove_rear():从队尾删除一个item元素
    -is_empty():判断双端队列是否为空
    -size():返回队列的大小

-remove_front():从队头删除一个item元素
-remove_rear():从队尾删除一个item元素
-is_empty():判断双端队列是否为空
-size():返回队列的大小
class Deque(object):
	def __init__(self):
		self.__items=[]
	def add_front(self,item):
		'''从队头加入一个item元素'''
		self.__items.insert(0,item)
	def add_rear(self,item):
		'''从队尾加入一个item元素'''
		self.__items.append(item)
	def remove_front(self):
		'''从队头删除一个item元素'''
		self.__items.pop(0)
	def remove_rear(self):
		'''从队尾删除一个item元素'''
		self.__items.pop()
	def is_empty(self):
		'''判断队列是否为空'''
		return self.__items==[]
	def size(self):
		'''返回队列的大小'''
		return len(self.__items)

排序与搜索

  • 排序算法sorting algorithm:一种将一串数据按照特定顺序进行排列的一种算法
  • 排序算法的稳定性:让原本有相等键值的记录维持相对稳定的次序
    -将不稳定的算法实现稳定:人工扩充键值的比较<会额外的空间负担>

冒泡排序

  • 冒泡排序Bubble sort:重复遍历要排序的数列,一次比较两个元素,如果顺序错误进行交换。
  • 时间复杂度
    -最优时间复杂度Q(n)<即遍历一次后发现没有任何可以交换的元素,排序结束>
    -最坏时间复杂度Q(n^2)
    -稳定性:稳定
def bubble(alist):
	for j in range(len(alist)-1,0,-1):
		for i in range(j):
			count=0
			if alist[i]>alist[i+1]:
				alist[i],alist[i+1]=alist[i+1],alist[i]
				count+=1
		if count==0:
			return
#另一种方式
def bubble(object):
	for j in range(len(object)-1):
		for i in range(len(object)-1-j):
			if object[i]>object[i+1]:
				object[i],object[i+1]=object[i+1],object[i]

选择排序

  • 选择排序selction sort:依次在未排序序中找到最小的元素,依次放到排序序列中。
  • 优点:与数据移动相关,若某个元素处于正确的位置,则不会进行移动。在完全依靠交换去移动元素的排序中,选择排序是一种较好的方法。
  • 时间复杂度
    -最优时间复杂度:O(n^2)
    -最坏时间复杂度:O(n^2)
    -稳定性:不稳定<考虑升序每次选择最大的情况>
def select_sort(alist):
	n=len(alist)
	for j in range(n-1):
		min_index=j
		for i in range(j+1,n):
			if alist[i]<alist[min_index]:
				min_index=i
		alist[j],alist[min_index]=alist[min_index],alist[j]

插入排序

  • 插入排序insertion sort:通过构建有序序列,对于未排序的序列,在已排序的序列中从后往前扫描,找到相应的位置插入。
  • 时间复杂度:
    -最优时间复杂度:O(n)<序列已经处于升序的状态>
    -最坏时间复杂度:O(n^2)
    -稳定性:稳定
#最坏情况
def insertion_sort(object):
	n=len(object)
	for i in range(1,n):
		for j in range(i,0,-1):
			if object[j]<object[j-1]:
				object[j],object[j-1]=object[j-1],object[j]

#优化
def insertion_sort(object):
	n=len(object)
	for i in range(1,n):
		j=i
		while j>0:
			if object[j]<object[j-1]:
				object[j],object[j-1]=object[j-1],object[j]
				j-=1
			else:
				break
			

快速排序

  • 快速排序:
  • 时间复杂度:
    -最优时间复杂度:O(nlogn)
    -最坏时间复杂度:O(n^2)
    -稳定性:不稳定
def quick_sort(alist,start,end):
	'''快速排序‘’‘
	if start>=end:
		return
	mid_value=alist[start]
	low=start
	high=end
	while low<high:
		while low<high and alist[high]>=mid_value:
			high-=1
		alist[low]=alist[high]
		while low<high and alist[low]<mid_value:
			low+=1
		alist[high]=alist[low]
	alow[low]=mid_value
	quick_sort(alist,first,low-1) #对low左边列表进行排列
	quick_sort(alist,low+1,last)

希尔排序

  • 希尔排序:是插入排序的一种,即缩小增量排序,是直接插入排序的一种更高效的方法。把记录按下标的一定增量分组,对每组使用直接插入排序算法排序;当增量减至1,算法结束。
  • 时间复杂度:
    -最优时间复杂度:根据步长序列的不同而不同
    -最坏时间复杂度:O(n^2)
    -稳定性:不稳定

def shell_sort(alist):
	n=len(alist)
	gap=n//2
	while gap>0:
		for i in range(gap,n):
			j=i
			while j >0:
				if alist[j]>alist[j-gap]
					alist[j],alist[j-gap]=alist[j-gap],alist[j]
					j-=gap
				else:
					break
		gap//2

归并排序

  • 归并排序:分治法的一种应用,先递归后分解数组,再合并数组。
  • 时间复杂度:
    -最优时间复杂度:O(nlogn)
    -最坏时间复杂度:O(nlogn)
    -稳定性:稳定
def merge_sort(alist):
	if len(alist)<=1:
		return alist
	num=len(list)/2
	left=merge_sort(alist[:num])
	right=merge_sort(alist[num:])	
	left_pointer,right_pointer=0,0
	result=[]
	while left_pointer<len(left) and right_pointer<len(right):
		if left[left_pointer]<right[right_pointer]:
			result.append(left[left_pointer])
			left_pointer+=1
		else:
			result.append(right[right_pointer])
			right_pointer+=1
	result+=left[left_pointer:]
	result+=right[right_pointer:]
	return result

常见排序算法效率比较


-!!快读排序最重要!!

搜索

  • 搜索:在一个项目集合中找到一个特定项目的算法过程。常见的查找方法:顺序查找、二分法查找、二叉树查找、哈希查找
  • 二分查找:即折半查找,适用于不经常变动而频繁查找的有序列表。优点:查找次数少、平均性能好,缺点:必须为有序表。
  • 时间复杂度:
    -最优时间复杂度O(1)
    -最坏时间复杂度O(logn)
# 递归
def binary_search(alist,item):
	n=len(alist)
	mid=n/2
	if alist[mid]==item:
		return Ture
	elif alist[mid]<item:
		return binary(alist[mid+1:],item)
	else:
		return binary(alist[:mid],item)
	return False
#非递归
def binary_search(alist,item):
	n=len(alist)
	low=0
	high=n-1
	while low<=high:
		mid=(low+high)//2
		if alist[mid]==item:
			return Ture
		elif alist[mid]<item:
			low=mid+1
		else:
			high=mid-1
	return False

树与算法

  • 树tree:抽象数据类型ADT或实作这种抽象数据类型的数据结构。由n个有限节点组成具有层次关系的集合。
    -每个节点有零个或多个子节点
    -没有父节点的节点为根节点
    -每一个非根节点有且只有一个父节点
    -除了根节点外,每个子节点可分为多个不相交的子树
  • 术语
    -节点的度:一个节点含有子树的个数
    -树的度:最大节点的度称为树的度
    -叶节点或终端节点:度为零的节点
    -父节点:若一个节点含有子节点,则该节点为子节点的父节点
    -子节点:一个节点含有的子树的根节点称为该节点的子节点
    -兄弟节点:具有相同父节点的节点称为兄弟节点
    -节点的层次:从根节点开始定义,依次为第一层、第二层、第三层。。。
    -树的高度或深度:树中节点的最大层次
    -堂兄弟节点:父节点在同一层次的节点互为堂兄弟节点
    -节点的祖先:从根到该节点所经分支上的所有节点
    -子孙:以某节点为根的子树中任意节点称为该节点的子孙
    -森林:由m根互不相交的树的集合称为森林
  • 树的种类
    -无序树:树中任意节点的子节点没有顺序关系,即无序树(自由树)
    -有序树:树中任意节点的子节点之间有顺序关系,即有序树
    a)二叉树:那个节点最多含有两个子树的树称为二叉树
    1)完全二叉树:假设深度为d的二叉树,除d层外,其余各层的节点数目均以达到最大值,且第d层所有节点从左到右紧密排列,即完全二叉树。其中满二叉树即所有也节点均在底层的二叉树
    2)平衡二叉树:AVL树,当任何节点的两颗子树的高度差不大于1的二叉树
    3)排序二叉树<二叉查找树>:即二叉搜索树、有序二叉树
    b)雷夫曼树<用于信息编码>:即最优二叉树,带权路径最短的二叉树
    c)B树:一种对读写操作进行优化的自平衡二叉查找树,能够保持数据的有序,拥有多余两个子树
  • 树的存储与表示
    -顺序存储:将数据结构存储在固定的数组中,遍历速度具有一定的优势,但所占空间结构比较大,是非主流二叉树。
    -链式存储:二叉树通常以链式存储。缺点<指针域指针个数不定>-<将多叉树转为二叉树>
    !!由于树的节点个数无法把握,因此将常见树的存储表示转换为二叉树进行处理。
  • 常见的一些树的应用场景:xml、html等;路由协议;mysql数据库索引;文件系统的目录结构、AI算法如决策树

二叉树

  • 二叉树:每个节点最多有两个子节点,通常子树被称为左子树left subtree或右子树right subtree
  • 二叉树的性质:
    -性质一:在二叉树的第i层上至多有2^(i-1)个结点
    -性质二:深度为k的二叉树至多有2^k-1个结点
    -性质三:对于任意一颗二叉树,如果其叶结点数为n,度数为2的结点总数为m,则n=m+1
    -性质四:具有n个节点的完全二叉树的深度为log(n+!)<2为底>
    -性质五:对于完全二叉树,若从下至上、从左到右编号,则编号为i的结点,其左子编号必须为2i,其右子编号必须为21+1,其双亲的编号必须为i/2<i=1根节点除外>
    -二叉树的节点表示及树的创建
class Node(object):
	''‘节点的表示’‘’
	def __init__(self,item):
		self.elem=item
		self.lchild=None
		self.rchild=None
class Tree(object):
	def __init__(self):
		self.root=None
	def add(self,item):
		node=Node(item)
		if self.root is None:
			self.root=node
			return
		queue=[self.root]
		while queue:
			cur_node=queue.pop(0)
			if cur_node.lchild is None:
				cur_node.lchild=node
				return 
			else:
				queue.append(cur_node.lchild)
			if cur_node.rchild is None:
				cur_node.rchild=node
				return 
			else:
				queue.append(cur_node.rchild)

二叉树的遍历

  • 遍历traversal:即对树中所有节点的信息进行访问。树的两种重要的遍历模式:深度优先模式和广度优先遍历,深度优先一般用递归,广度优先一般用队列。
  • 广度优先遍历<层次遍历>:从上到下、从左到右
def breadth_travel(self):
	'''利用队列实现树的层次遍历‘’‘
	if self.root is None:
		return 
	queue=[self.root]
	while queue:
		cur_node=queue.pop(0)
		if cur_node.lchild is not None:
			queue.qppend(cur_node.lchild)
		if cur_node.rchild is None:
			queue.append(cur_node.rchild)
	return 
  • 深度遍历depth first search:沿着树的s
    -主要由三种方式常用于访问树的节点,区别在于访问节点的次序不同。
    -先序遍历preorder:根节点-左子树-右子树
    -中序遍历inorder:左子树-根节点-右节点
    -后序遍历postorder:左节点-右节点-根节点
#先序遍历
def preorder(self,node):
	if node is None:
		return
	print(node.elem,end=‘ ’)
	preorder(node.lchild)
	preorder(node.rchild)
	
#中序遍历
def inorder(self,node):
	if node is None:
		return
	inorder(node.lchild)
	print(node.elem,end=‘ ’)
	inorder(node.rchild)

#后序遍历
def postorder(self,node):
	if node is None:
		return
	postorder(node.lchild)
	postorder(node.rchild)
	print(node.elem,end=‘ ’)
	
  • 由遍历结果确定一棵树:必须包含中序
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值