常用数据结构
大学打了四年游戏,没想到还要继续把数据结构捡起来。
一、数据结构概念
1、数组
数组是可以再内存中连续存储多个元素的结构,在内存中的分配也是连续的,数组中的元素通过数组下标进行访问,数组下标从0开始。
2、栈
栈是一种特殊的线性表,仅能在线性表的一端操作,栈顶允许操作,栈底不允许操作。 栈的特点是:先进后出 (先进去的数据是栈底)
3、队列
队列与栈一样,也是一种线性表,不同的是,队列可以在一端添加元素,在另一端取出元素,也就是:先进先出(先进去的数据是队首)
4、链表
链表是物理存储单元上非连续的、非顺序的存储结构,数据元素的逻辑顺序是通过链表的指针地址实现,
每个元素包含两个结点,一个是存储元素的数据域 (内存空间),另一个是指向下一个结点地址的指针域
根据指针的指向,链表能形成不同的结构,例如单链表,双向链表,循环链表等
5、树
树是一种数据结构,它是由n(n>=1)个有限节点组成一个具有层次关系的集合
6、散列表
散列表,也叫哈希表,是根据关键码和值 (key和value) 直接进行访问的数据结构,通过key和value来映射到集合中的一个位置,这样就可以很快找到集合中的对应元素
7、堆
堆是一种比较特殊的数据结构,可以被看做一棵树的数组对象,具有以下的性质:堆中某个节点的值总是不大于或不小于其父节点的值;
堆总是一棵完全二叉树。将根节点最大的堆叫做最大堆或大根堆,根节点最小的堆叫做最小堆或小根堆。常见的堆有二叉堆、斐波那契堆等
8、图
图是由结点的有穷集合V和边的集合E组成。其中,为了与树形结构加以区别,在图结构中常常将结点称为顶点,边是顶点的有序偶对,
若两个顶点之间存在一条边,就表示这两个顶点具有相邻关系。
二、数据结构相关算法
1、链表
a、链表初始化、头插法、尾插法
# 初始化链表 class Node: def __init__(self, item): self.item = item self.next = None # 链表的创建 # 头插法: def create_linklist_head(li): head = Node(li[0]) # 头结点 for element in li[1:]: node = Node(element) # 先把元素变为node对象 node.next = head # node对象的next属性设置为 头结点 head = node # 再把node对象置为头结点 return head # 尾插法: def create_linklidt_tail(li): head = Node(li[0]) tail = head for element in li[1:]: node = Node(element) tail.next = node tail = node return head # 返回头结点因为头结点是第一个结点 # 遍历输出链表: def print_linklist(li): while li: print(li.item, end=',') li = li.next print(end='\n') li = create_linklist_head([1, 2, 3, 6, 8]) print_linklist(li) lk = create_linklidt_tail([1, 2, 3, 6, 8]) print_linklist(lk)
b、反转链表
# class ListNode: # def __init__(self, x): # self.val = x # self.next = None # # 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可 # # # @param head ListNode类 # @return ListNode类 # class Solution: def ReverseList(self , head: ListNode) -> ListNode: # write code here pre_head= None # 反转链表的头节点 while head: temp = head.next # 先把head的next属性存起来 head.next = pre_head # 把head的next属性 变更为 pre_head pre_head = head # 再把head置为反转链表的头结点 # 第一轮pre_head为 val=1,next=None # 第二轮pre_head为 val=2,next=1 # 第三轮pre_head为 val=3,next=2 # 第四轮pre_head为 val=4,next=3 head = temp # 再把之前保存的next赋值给head,进入下一个循环 return pre_head
2、二叉树前序、中序、后序遍历
前序遍历:根节点->左子树->右子树(根->左->右)
中序遍历:左子树->根节点->右子树(左->根->右)
后序遍历:左子树->右子树->根节点(左->右->根)
a、二叉树的深度
# class TreeNode: # def __init__(self, x): # self.val = x # self.left = None # self.right = None # # 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可 # # # @param pRoot TreeNode类 # @return int整型 # class Solution: def TreeDepth(self , pRoot: TreeNode) -> int: # write code here if pRoot is None: return 0 # 每max一次+1,max的次数等于递归深度,最终max(0,0) return max(self.TreeDepth(pRoot.left), self.TreeDepth(pRoot.right))+1
b、二叉树的镜像
# class TreeNode: # def __init__(self, x): # self.val = x # self.left = None # self.right = None # # 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可 # # # @param pRoot TreeNode类 # @return TreeNode类 # class Solution: def Mirror(self , pRoot: TreeNode) -> TreeNode: # write code here if pRoot is None: return None; pRoot.left,pRoot.right = pRoot.right,pRoot.left; self.Mirror(pRoot.left) self.Mirror(pRoot.right) return pRoot
3、用两个栈实现队列
class MyQueue(object): def __init__(self): self.stack1 = [] self.stack2 = [] def push(self, x): self.stack1.append(x) def pop(self): # 如果2为空,就把1的数据压到2; # 不为空,就把2的数据删完 if not self.stack2: while self.stack1: self.stack2.append(self.stack1.pop()) return self.stack2.pop() # 删除2最后一条数据 def peek(self): if not self.stack2: # 如果为空,就把1的数据压到2 while self.stack1: self.stack2.append(self.stack1.pop()) return self.stack2[-1] # 取2最后一条数据 def empty(self): return not self.stack1 and not self.stack2
4、包含min函数的栈
# -*- coding:utf-8 -*- class Solution: def __init__(self): self.stack1 = [] self.stack2 = [] def push(self, node): # write code here self.stack1.append(node) def pop(self): # return xx # 如果stack2有元素,则先pop。 # 不这样做的话,会把最近一次的push给返回出去了,不符合先进先出原则 if self.stack2: return self.stack2.pop() while self.stack1: self.stack2.append(self.stack1.pop()) return self.stack2.pop()
5、栈的弹出判断
# # 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可 # # # @param pushV int整型一维数组 # @param popV int整型一维数组 # @return bool布尔型 # class Solution: def IsPopOrder(self, pushV: List[int], popV: List[int]) -> bool: # write code here n = len(pushV) s = [] # 辅助栈 j = 0 # 入栈下标 for i in popV: # 遍历出栈 # 辅助栈为空或栈顶不等于出栈,则把数据填入辅助栈 while (not s or s[-1] != i) and j < n: s.append(pushV[j]) j += 1 # 栈顶等于出栈,则删掉栈顶,进入下一个循环 if s[-1] == i: s.pop() else: return False return True
6、滑动窗口的最大值(双向队列)
# # 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可 # # # @param num int整型一维数组 # @param size int整型 # @return int整型一维数组 # class Solution: def maxInWindows(self , num, size: int) : # write code here res = [] #窗口大于数组长度的时候,返回空 if size <= len(num) and size != 0: from collections import deque #双向队列 dq = deque() # 先遍历一个窗口,取最大值的下标存进dp for i in range(size): while len(dq) != 0 and num[dq[-1]] < num[i]: dq.pop() dq.append(i) # 遍历后续数组元素 for i in range(size, len(num)): res.append(num[dq[0]]) # 如果队首不在窗口内,则移除队首。直到队首满足在窗口内跳出循环 while len(dq) != 0 and dq[0] < (i - size + 1): dq.popleft() # 如果即将进入队列的下标的值(num[i] )大于队列后方的值,依次将小于的值拿出来去掉 while len(dq) != 0 and num[i] > num[dq[-1]]: dq.pop() dq.append(i) # 最后一次循环没有把最后一组窗口的最大值加进数组,所以还需要加一次 res.append(num[dq[0]]) return res
7、给你一个整数数组nums、一个整数limit,要求子数组中任意两个元素差值小于等于limit,返回最长连续子数组的长度。【双指针】
class Solution: def longestSubarray(self, nums, limit: int) -> int: from sortedcontainers import SortedList # 创建一个有序数组,sortedcontainers模块需要安装 s = SortedList() left, right, res = 0, 0, 0 while right < len(nums): s.add(nums[right]) # 如果最大最小值之差大于限制,有序数组就删除left指向的值,直到left满足条件 # 有序数组如果有相同元素,只会删除第一个 while s[-1] - s[0] > limit: s.remove(nums[left]) left += 1 # 当前子数组的长度1 + right - left ; 每循环一次,更新最大值 res = max(res, 1 + right - left) right += 1 return res