常用数据结构与算法总结

# ctrl+/ 注释当前选中代码块  
"""
    cell
    ctrl+enter   运行当前的cell
    shift+enter  运行当前cell并跳转到下一个cell   爬虫   web后台   数据分析   机器学习 
    dd:删除当前选中的cell
    a:当前cell上方创建一个cell
    b:当前cell下方创建一个cell
"""
1+2
3
# String相关操作
s1=str()
s2="tanghaijun"
# tab  代码提示
s2len=len(s2)
print(s2len)
s2[0:4]
s2[7:10]
s2[-3:]
s2[:5]
s3=s2[0:4]
display(s3)
s3+='jun'
display(s3)
s3list=list(s3)
display(s3list)
display(s2[4])
display(s2.index('i'))#return i所在位置
# display(s2.index('w'))#return w所在位置  没有w会抛异常
display(s2.find('w'))#return w所在位置 not found return -1
display(s2.find('i'))
10



'tang'



'tangjun'



['t', 'a', 'n', 'g', 'j', 'u', 'n']



'h'



6



-1



6
"""
Java版字符串操作
String s1 = new String();
String s2 = "billryan";
int s2Len = s2.length();
s2.substring(4, 8); // return "ryan"
StringBuilder s3 = new StringBuilder(s2.substring(4, 8));
s3.append("bill");
String s2New = s3.toString(); // return "ryanbill"
// convert String to char array
char[] s2Char = s2.toCharArray();
// char at index 4
char ch = s2.charAt(4); // return 'r'
// find index at first
int index = s2.indexOf('r'); // return 4. if not found, return -1

StringBuffer 与 StringBuilder, 前者保证线程安全,后者不是,但单线程下效率⾼⼀些,⼀般使⽤
StringBuilder
"""
#链表  Linked List
"""
    链表是一种最基本的,最简单的数据结构,元素之间的关系是一对一的关系,
    除了第一个和最后一个元素之外,其他数据元素都是收尾相接,线性表有两种
    存储方式,一种顺序存储结构,另外一种就是链式存储结构,常见的数组就是
    顺序存储,集合就是链式存储
    
    相反,链式存储结构就是两个相邻的元素在内存中可能不是相邻的,每⼀个元素都有⼀个指针域,指
    针域⼀般是存储着到下⼀个元素的指针。这种存储⽅式的优点是定点插⼊和定点删除的时间复杂度为
    O(1),不会浪费太多内存,添加元素的时候才会申请内存,删除元素会释放内存。缺点是访问的时间
    复杂度最坏为 O(n)。
    顺序表的特性是随机读取,也就是访问⼀个元素的时间复杂度是O(1),链式表的特性是插⼊和删除的
    时间复杂度为O(1)。
    链表就是链式存储的线性表。根据指针域的不同,链表分为单向链表、双向链表、循环链表等等。

"""
'\n    链表是一种最基本的,最简单的数据结构,元素之间的关系是一对一的关系,\n    除了第一个和最后一个元素之外,其他数据元素都是收尾相接,线性表有两种\n    存储方式,一种顺序存储结构,另外一种就是链式存储结构,常见的数组就是\n    顺序存储,集合就是链式存储\n'
"""
    时间复杂度的阶有:
        O(1):常量的时间阶
        O(n):线性的时间阶
        O(log n):对数的时间阶
        O(nlogn):线性对数时间阶
        O(n^k): k>=2  k次方的时间阶
"""
# O(1)常量时间阶
1+1
2+2
4+4
8
# 线性时间阶  O(n)  
"""
算法中基本操作重复执行的次数是问题规模n的某个函数,其时间
度量记作T(n)=O(f(n))称作算法的渐进时间复杂度,简称时间复杂度
"""
for(i=1;i<=n;i++){
    print(i)
}
  File "", line 6
    for(i=1;i<=n;i++){
         ^
SyntaxError: invalid syntax
# n^2  时间复杂度T(n)=O(n^2)
for(i=1;i<=n;i++){
    for(j=1;j<=n;j++){
        print(c[i][j])
    }
}
  File "", line 2
    for(i=1;i<=n;i++){
         ^
SyntaxError: invalid syntax
"""
    空间复杂度:指算法编写成程序后,在计算机中运行时所需要存储
    空间大小的度量,记作S(n)=O(f(n))
    其中n为问题的规模(或者大小)
    存储空间一般包含三个方面:
    -指令常数变量所占用的存储空间
    -输入数据所占用的存储空间
    -辅助(存储空间)
        一般的 算法的空间指的就是辅助空间
    一维数组 a[n]   空间复杂度O(n)
    二维数组 a[n][m]   空间复杂度O(n*m)
"""
# 链表Python编程实现
class ListNode:
    def __init__(self,val):
        self.val=val;
        self.next=None
"""
    C++实现
    struct ListNode {
        int val;
        ListNode *next;
        ListNode(int val,ListNode *next=NULL):val(val),next(next){}
    };
    
    Java实现
    public class ListNode {
        public int val;
        public ListNode next;
        public ListNode(int val) {
            this.val = val;
            this.next = null;
        }
    }
"""
##链表的基本操作
"""
    反转链表
    1->2->3->null    3->2->1->null
    ①访问某个节点curt.next时,要检验curt是否为null
    ②要把反转后的最后一个节点(即反转前的第一个节点指向null)
"""
'\n    反转链表\n    1->2->3->null    3->2->1->null\n    ①访问某个节点curt.next时,要检验curt是否为null\n    ②要把反转后的最后一个节点(即反转前的第一个节点指向null)\n'
# Python反向链表python实现

class ListNode:
    def __init__(self, val):
        self.val = val
        self.next = None
# in python next is a reversed word
    def reverse(self, head):
        prev = None
        while head:
            temp = head.next
            head.next = prev
            prev = head
            head = temp
        return prev
"""
    C++实现
    ListNode * ReverseList(ListNode *head){
        ListNode *pre=NULL,*tmp;
        while(head){
            tmp=head->next;
            head->next=pre;
            pre=head;
            head=tmp;
        }
        return pre;
    }
    Java实现
    class ListNode {
        int val;
        ListNode next;
        ListNode(int val) {
            this.val = val;
        }
    }
    // iterative method
    public ListNode reverse(ListNode head) {
        ListNode prev = null;
        while (head != null) {
            ListNode next = head.next;
            head.next = prev;
            prev = head;
            head = next;
        }
        return prev;
    }
    // recursive method
    public ListNode reverse(ListNode head) {
        if (head == null || head.next == null) {
            return head;
        }
        ListNode next = head.next;
        ListNode newHead = reverse(next);
        next.next = head;
        head.next = null;
        return newHead;
    }
"""
"""
    双向链表跟单向链表的核心在于next和prev域的交换,还需要注意是当前节点和上一个节点的递推
"""
'\n    双向链表跟单向链表的核心在于next和prev域的交换,还需要注意是当前节点和上一个节点的递推\n'
# python实现
class DListNode:
    def __init__(self, val):
        self.val = val
        self.prev = self.next = null
    def reverse(self, head):
        curt = None
        while head:
            curt = head
            Linked List
            head = curt.next
            curt.next = curt.prev
            curt.prev = head
        return curt

"""
Java实现
class DListNode {
    int val;
    DListNode prev, next;
    DListNode(int val) {
        this.val = val;
        this.prev = this.next = null;
    }
}
    public DListNode reverse(DListNode head) {
        DListNode curr = null;
        while (head != null) {
            curr = head;
            head = curr.next;
            curr.next = curr.prev;
            curr.prev = head;
        }
        return curr;
    }

"""
# 二叉树
"""
    二叉树是每个节点最多有两个子树的树结构,子树有左右之分,
    二叉树常被用于实现二叉查找树和二叉堆
    
    满二叉树:一个深度为K,且有2^k-1个节点称之为满二叉树
    完全二叉树:深度为k,有n个节点的二叉树,当且仅当其每一个节点都与深度为k
    的满二叉树中序号为1到n的节点对应时,称之为完全二叉树(若设二叉树的深度
    为h,除第 h 层外,其它各层 (1~h-1) 的结点数都达到最大个数,第 h 层所有
    的结点都连续集中在最左边,这就是完全二叉树)
"""
'\n    二叉树是每个节点最多有两个子树的树结构,子树有左右之分,\n    二叉树常被用于实现二叉查找树和二叉堆\n'
# 二叉树的编程实现
class TreeNode:
    def __init__(self,val):
        self.val=val
        self.left,self.right=None,None
"""
C++实现
struct TreeNode {
    int val;
    TreeNode *left;
    TreeNode *right;
    TreeNode(int x) : val(x), left(NULL), right(NULL) {}
};
Java实现
public class TreeNode {
    public int val;
    public TreeNode left, right;
    public TreeNode(int val) {
        this.val = val;
        this.left = null;
        this.right = null;
    }
}
"""
"""
    树的遍历:
        按照访问根元素的前后顺序,遍历方式可以划分为:
        ①深度优先
                1.前序(pre-order):先根后左再右
                2.中序(in-order):先左后根再右
                3.后序(post-order):先左后右再根
        ②广度优先:先访问根节点,沿着树的宽度遍历子节点,直到所有的节点均被
                    访问为止(从左往右 从上往下)
    
    ⼆叉树的⼴度优先遍历和树的前序/中序/后序遍历不太⼀样,前/中/后序遍历使⽤递归,也就是栈的思
    想对⼆叉树进⾏遍历,⼴度优先⼀般使⽤队列的思想对⼆叉树进⾏遍历。
"""
'\n    树的遍历:\n        按照访问根元素的前后顺序,遍历方式可以划分为:\n        ①深度优先\n                1.前序(pre-order):先根后左再右\n                2.中序(in-order):先左后根再右\n                3.后序(post-order):先左后右再根\n        ②广度优先:先访问根节点,沿着树的宽度遍历子节点,直到所有的节点均被\n                    访问为止\n'
# 递归   递归一定要有出口
def factorial(n):
    if(n<=1):
        return 1
    else:
        subSolution=factorial(n-1)  
        solution=subSolution*n
        return solution
    
factorial(6)
720
# 遍历  前序  中序   后序
class Traversal(object):
#   类的方法与普通的函数只有一个特别的区别——它们必须有一个额外的第一个参数名称, 按照惯例它的名称是 self
    def __init__(self):
        self.traverse_path=list()
        
    def preorder(self,root):
        if root:
            selft.traverse_path.append(root.val)
            self.preorder(root.left)
            self.preorder(root.right)
    def inorder(self,root):
        if root:
#             self.traverse_path.append(root.left)  B ×
            self.inorder(root.left) #A.left=B  B.left=D  E.left  C.left=F  F.left
            self.traverse_path.append(root.val)  #B.val=B  E.val=E  A.val=A   F.val=F  C.val=C
            self.inorder(root.right) # B.right=E  A.right=C  F.right C.right
    def postorder(self,root):
        if root:
            self.postorder(root.left)# A.left=B  B.left=D  D.left  E.left
            self.postorder(root.right)#D.right  B.right=E  E.right
            self.traverse_path.append(root.val)# D.val=D   E.val=E  B.val=B
        
#     self.traverse_path=  DEB
# 树类题的复杂度分析
# 对树相关的题进⾏复杂度分析时可统计对每个节点被访问的次数,进⽽求得总的时间复杂度
"""
    Binary Search Tree 二叉查找树(BST)
    是一棵二叉树,其中每个节点都含有一个可进行比较的键及相应的值,且每个
    节点的键都大于等于左子树中任意节点的键
    可参考网址:https://www.cs.usfca.edu/~galles/visualization/BST.html
"""
'\n    Binary Search Tree 二叉查找树(BST)\n    是一棵二叉树,其中每个节点都含有一个可进行比较的键及相应的值,且每个\n    节点的键都大于等于左子树中任意节点的键\n'
"""
    压缩算法Huffman算法
    01100010  01100101 ....
    beep boop beer!
    
    字符	次数
    'b'	3
    'e'	4
    'p'	2
    ''	2
    'o'	2
    'r'	1
    '!'	1
    然后将这些东西放到Priority Queue中(用出现的次数当priority)可以看到
    priority Queue是以Priority排序的一个数组,如果Priority一样,会使用
    出现的次序排序
    
    接下来通过算法把这个priority Queue转化成二叉树,始终从queue的头
    取两个元素来构造一个二叉树(第一个元素是左节点,第二个元素是右节点)
    并把这两个元素的priority相加,并且放回到priority中(priority就是
    字符出现的次数)
    
    最终得到编码表:
    字符        编码
    b           00
    e           11
    p           101
    ''          010
    !           1001
    o           011
    r           1000
    注意:encode时候按照bit来进行encode,decode也是通过bit来完成
    比如:有这样的bitset 1011110111  解码后就是唯一的字符pepe
    
    pop        101011101
    1011110111 pepe       
"""
"""
    a
    0
    ab
    0  1
    abc
    00 01 10
    abcd
    00 01 10 11

    ASCII   8*20=160
    
    简单编码
    e      v     r    y   d   a  ''  i  s  w  o  m  !
    0000  0001  0010 ....
    
    everyday is awesome!
"""
"\n    a\n    0\n    ab\n    0  1\n    abc\n    00 01 10\n    abcd\n    00 01 10 11\n\n    ASCII   8*20=160\n    \n    简单编码\n    e      v     r    y   d   a  ''  i  s  w  o  m  !\n    0000  0001  0010 ....\n    \n    everyday is awesome!\n"
class SimpleCompression:
    def __init__(self,string):
        self.symbols=set(string)
#         display(self.symbols)
        self.bit_len=1
#         计算出当前字符串所需要的编码的字节长度
        while 2**self.bit_len<len(self.symbols):
            self.bit_len+=1
        self.string=string
        self.s2b={}
        self.b2s={}
        i=0
        for s in self.symbols:
#             将i转化成二进制
            b=bin(i)[2:]
#             在b字节保存长度为4,长度不够4则在前面补上0
            if len(b) < self.bit_len:
                b=(self.bit_len-len(b))*'0'+b
#             字符串转化成字节,保存在字典中,以键值对形式存储  字符是key  字节是value
            self.s2b[s]=b
#             字节转化成字符串,保存在字典中,以键值对形式存储  字节是key  字符是value
            self.b2s[b]=s
            i+=1
#         返回出字符串对应的二进制编码
    def compress(self):
        bits=''
        for s in self.string:
            bits+=self.s2b[s]
        return bits
    
    def uncompress(self,bits):
        string =''
        for i in range(0,len(bits),self.bit_len):
            string+=self.b2s[bits[i:i+self.bit_len]]
        return string
import heapq
import collections

collections.Counter("ssadafagsfda")
Counter({'a': 4, 'd': 2, 'f': 2, 'g': 1, 's': 3})
import heapq
import collections
class HuffmanCompression:
    class Trie:
        def __init__(self, val, char=''):
            self.val = val
            self.char = char
            self.coding = ''
            self.left = self.right = None

        def __eq__(self, other):
            return self.val == other.val

        def __lt__(self, other):
            return self.val < other.val

        def __gt__(self, other):
            return self.val > other.val

    def __init__(self, string):
        self.string = string
        counter = collections.Counter(string)
#         display(counter)
        heap = []
        for char, cnt in counter.items():
            heapq.heappush(heap, HuffmanCompression.Trie(cnt, char))
        while len(heap) != 1:
            left = heapq.heappop(heap)
            right = heapq.heappop(heap)
            trie = HuffmanCompression.Trie(left.val + right.val)
            trie.left, trie.right = left, right
            heapq.heappush(heap, trie)

        self.root = heap[0]
        self.s2b = {}
        self.bfs_encode(self.root, self.s2b)

    def bfs_encode(self, root, s2b):
        queue = collections.deque()
        queue.append(root)
        while queue:
            node = queue.popleft()
            if node.char:
                s2b[node.char] = node.coding
                continue
            if node.left:
                node.left.coding = node.coding + '0'
                queue.append(node.left)
            if node.right:
                node.right.coding = node.coding + '1'
                queue.append(node.right)

    def compress(self):
        bits = ''
        for char in self.string:
            bits += self.s2b[char]
        return bits

    def uncompress(self, bits):
        string = ''
        root = self.root
        for bit in bits:
            if bit == '0':
                root = root.left
            else:
                root = root.right
            if root.char:
                string += root.char
                root = self.root
        return string
def get_rate(compressed_binary,uncompressed_bits):
    return len(compressed_binary)*100/uncompressed_bits
if __name__ == '__main__':
    s='everyday is awesome!'
    #ASCII
    bits=len(s)*8
    print('Total bits :%d'%bits)
#     simple compression
    sc=SimpleCompression(s)
    compressed=sc.compress()
    print('Compressed binary:'+compressed)
    print('Uncompressed :'+sc.uncompress(compressed))
    print(sc.s2b)
    print('Simple Compression-compress rate: %d%%'%get_rate(compressed,bits))
    
    print('--------------------Huffman compression-------------------')
    hc=HuffmanCompression(s)
    compressed=hc.compress();
    print('Compressed binary:'+compressed)
    print("UnCompressed:"+hc.uncompress(compressed))
    print(hc.s2b)
    print("Huffman Compression-compress rate :%d%%"%get_rate(compressed,bits))
Total bits :160
Compressed binary:10011011100100000010000101100010101000110100101001101000100101000111010110011100
Uncompressed :everyday is awesome!
{'r': '0000', 'd': '0001', 'y': '0010', 'i': '0011', 's': '0100', 'm': '0101', 'a': '0110', 'o': '0111', 'w': '1000', 'e': '1001', ' ': '1010', 'v': '1011', '!': '1100'}
Simple Compression-compress rate: 50%
--------------------Huffman compression-------------------
Compressed binary:011101001110111111000011101111110010110011100111000010100110101000011001
UnCompressed:everyday is awesome!
{'e': '01', 's': '001', 'd': '0000', 'w': '0001', 'm': '1000', '!': '1001', 'o': '1010', 'i': '1011', ' ': '1100', 'a': '1110', 'y': '1111', 'v': '11010', 'r': '11011'}
Huffman Compression-compress rate :45%
s="everyday is awesome!"
sc=SimpleCompression(s)
sc.uncompress("010000101100")
's'



'sy'



'sy!'





'sy!'
s="everyday is awesome!"
sc=SimpleCompression(s)
print(sc.compress())
{' ', '!', 'a', 'd', 'e', 'i', 'm', 'o', 'r', 's', 'v', 'w', 'y'}


10011011100100000010000101100010101000110100101001101000100101000111010110011100
2*'0'+'10'
'0010'
print(bin(0)[2:])
print(bin(1)[2:])
print(bin(2)[2:])
print(bin(3)[2:])
print(bin(4)[2:])
"""
0   0000
1   0001
10  0010
11  0011
100 0100
"""
0
1
10
11
100
len(set("everyday is awesome!"))
13
set("everyday is awesome!")
{' ', '!', 'a', 'd', 'e', 'i', 'm', 'o', 'r', 's', 'v', 'w', 'y'}
# Queue队列
"""
    (先进先出)的数据结构,并发中使用较多,可以安全地将对象从一个任务传给另一个任务
    编程实现:
    Queue和stack在python中都是有list,实现的,list是一个动态数组,可以通过append在list尾部
    添加元素,通过pop(0)在list首部弹出实现queue的FIFO
    stack  :  FILO   pop()尾部元素弹出实现stack操作 
    
"""
#same as list()
queue=[]
size =len(queue)
display(size)
queue.append(1)
display(queue)
queue.append(2)
display(queue)
display(queue[0])
queue.pop(0)
display(queue)
display(queue[0])
queue.append(3)
display(queue)
queue.pop()
display(queue)
0



[1]



[1, 2]



1



[2]



2



[2, 3]



[2]
"""
    Priority Queue 优先队列
    应用程序常常需要处理带有优先级的业务,优先级最高的业务首先得到服务因此优先队列这种数据
    结构应运而生。优先队列中每个元素都有自己的优先级,优先级最高的元素最先得到服务;优先级
    相同的元素按照其在优先队列中顺序得到服务
    
    优先队列可以使用数组或者链表实现,从时间和空间复杂度来说,往往用二叉堆来实现
    
    python中提供了heapq的lib来实现priority queue,提供push,pop两个基本操作和heapify初始
    化操作
    
    Java中提供了一个PriorityQueue类
"""
"""
                    methods     
enqueue             heapq.push(queue,e)
dequeue             heapq.pop(queue)
"""
"""
    Deque-双端队列
        双端队列可以让在任何一端添加或者移除元素,因此它是一种具有队列和栈性质的数据结构
        
    python:
        list就是可以执行类似于deque的操作,但是效率会比较慢,为了提高数据处理效率,
        collections中提供了deque的类,建议在用list时多次执行头尾操作,请使用deque
        
    Java:Deque类操作
"""
dq=collections.deque()

你可能感兴趣的:(数据结构与算法)