Python面试——数据结构

一、Python中内置的4种数据结构:列表list、元组tuple、字典dict、集合set

二、python中6种基础的数据类型包括:

  1 Number(数字)

  2 String(字符串)

  3 List(列表)

  4 Tuple(元组)

  5 set(集合)

  6 Pictionary(字典)

按照可变数据和不可变数据来区分:

  不可变数据(3个):Number(数字),String(字符串),Tuple(元组)
  可变数据(3个):List(列表),Dictonary(字典),set(集合)

创建方式:

  创建列表: listT=[1,2,3,4,5]

  创建元组:tup2=(1,2,3,4,5)

  创建字典:dict2={"abc":123,"def":789}

  创建集合:student={'Tom','Jim','Mary'}

三、列表

列表中元素的类型可以不相同,它支持数字,字符串甚至可以包含列表(所谓嵌套)【插入数据可重复,数据不唯一】有序

listT=[1,"2",(3,4,5,6),[7,8,9],{"10":11},{12,13,14}]    #列表可以存储数据类型:数字,字符,元组,列表,字典,set 
print listT #得:[1, '2', (3, 4, 5, 6), [7, 8, 9], {'10': 11}, set([12, 13, 14])]

列表操作包含以下函数:

1

2

3

4

5

1cmp(list1, list2):比较两个列表的元素

2len(list):列表元素个数

3max(list):返回列表元素最大值

4min(list):返回列表元素最小值

5list(seq):将元组转换为列表

列表操作包含以下方法:

1

2

3

4

5

6

7

8

9

1list.append(obj):在列表末尾添加新的对象

2list.count(obj):统计某个元素在列表中出现的次数

3list.extend(seq):在列表末尾一次性追加另一个序列中的多个值(用新列表扩展原来的列表)

4list.index(obj):从列表中找出某个值第一个匹配项的索引位置

5list.insert(index, obj):将对象插入列表

6list.pop(index):移除列表中的一个元素(默认最后一个元素),并且返回该元素的值

7list.remove(obj):移除列表中某个值的第一个匹配项

8list.reverse():反向列表中元素

9list.sort([func]):对原列表进行排序

四、元组

元组(tuple)与列表类似,元组不能二次赋值。【元组本身是只读对象,如果元组中的对象是可变类型数据,可变类型数据依然可以修改】有序

tuple=(1,"2",(3,4,5,6),[7,8,9],{"10":11},{12,13,14})    #元组可以存储数据类型:数字,字符,元组,列表,字典,set
print tuple
print tuple[0]
print tuple[3]
tuple[3][0]=77
print tuple[3]
一下为输出测试结果:

#(1, '2', (3, 4, 5, 6), [7, 8, 9], {'10': 11}, set([12, 13, 14]))
# 1
#[7, 8, 9]
#[77, 8, 9]

注意:当元组中元素只有一个时,结尾要加逗号,若不加逗号,Python解释器都将解释成元素本身的类型,而非元组类型。

# del  删除  (元素对象不支持删除,但是可以删除整个元组变量)

# del 删除元组中元素

up = ('tang', 'guo', 'li','xiu')
del tup[0]  #   元素对象不支持删除
# print (tup)

#输出结果
TypeError: 'tuple' object doesn't support item deletiontup = ('tang', 'guo', 'li','xiu')
del tup  #删除
print (tup) # del tup ,内存已删除,在打印将提示未定义

#输出结果
NameError: name 'tup' is not defined

# count 统计元素个数

# count 统计元素个数
tup = ('tang', 'guo', 'li','guo').count('guo')
print (tup)

#输出结果
2

#  index 返回元素的索引位置

# index 返回元素的索引位置
tup = ('tang', 'guo', 'li','xiu')
print (tup.index('li')) # 返回元素'ii'的索引位置

#输出结果
2

# len  计算元组中元素的个数

# len   计算元组中元素的个数
tup = ('tang', 'guo', 'li','xiu')
print (len(tup))    # 计算元组长度

#输出结果
4

1、当元组中一个元素时,一定要在元素后面加上逗号

2、元组中的元素是不允许删除的,但可以使用del语句来删除整个元组

3、元组没有列表中的增、删、改的操作,只有查的操作

五、字典

列表是有序的对象集合,字典是无序的对象集合【键必须是唯一的,但值则不必;值可以取任何数据类型,但键必须是不可变的】无序

dict2={1:1,"2":"2",(3):(3)}
print dict2   #得 {3: 3, 1: 1, '2': '2'} 元组类型发生了改变
dict2={1:1,"2":"2",(3,2):(3,2)}
print dict2  #{1: 1, (3, 2): (3, 2), '2': '2'} 元组未发生改变
dict2={1:1,"2":"2",(3,2):(3,2),4:[5],"5":{"66":"66"},(3):{"77","88"}}  #键必须是不可变的,值可以是任何类型:依次为数字,字符,元组,列表,字典,set集合
print dict2 #得 {1: 1, (3, 2): (3, 2), 3: set(['77', '88']), 4: [5], '2': '2', '5': {'66': '66'}}
info = {
    'stu1101': "安徽",
    'stu1102': "北京",
    'stu1103': "河南",
}
# 增删查改
info["stu1104"] = "浙江"   #增
info['stu1101'] = "中国"  #改
info.pop('stu1104')        #删
# del info['stu1104']       # 删
print(info.get('stu1106'))  # 查询,没有的话返回None
info.setdefault("stu1106","江苏") # 如果没有key,就设置值江苏,否则不变

# update
b = {1:2,3:4,'stu1102':'朝阳区'}
info.update(b)    # 合并更新两个字典,如果有相同的key,则更新此key的value,没有则添加键值对
print(info)

# 获取字典的键值对、字典的值、字典的键
print(info.items())
print(info.values())
print(info.keys())

# 遍历字典
for key in info:          # 更高效一些
    print(key,info[key])

for k,v in info.items(): # 先把字典转成list,数据大时不要用
    print(k,v)

# 对字典排序
a = {1:23,99:2,28:72,3:83,72:222}
print(sorted(a))           # 对字典按照key排序,打印key的list
print(sorted(a.items()))   # 对字典按照key排序,打印键值对元组,list类型
print(sorted(a.items(),key=lambda x:x[1],reverse=True))   # 对字典按照value排序,可以降序或升序,打印键值对元组

六、集合

set是一个无序的不重复元素序列    集合只能存放不可变类型,可变类型放入后会报错  无序

set={1,"2",(3,2)}
print set  # 得:set([1, (3, 2), '2']) ;看到数据结构后着实让人发现原来set集合内部结构也是一个列表。这就不难理解为啥可以用set给list列表去重
set={1,"2",(3,2)}      #集合只能存放不可变类型,可变类型放入后会报错
print set
list=[4,5,6]
dict2={"9":"10"}
set={6,7,8}
#set.add(list)   #TypeError: unhashable type: 'list'
#set.add(dict2)  #TypeError: unhashable type: 'dict'
#set.add(set)     #TypeError: unhashable type: 'set'

3、集合的特点:

    创建一个空集合必须用 set() 而不是 { },因为 { } 是用来创建一个空字典;
    set 里边的元素是不可重复的,所以集合可用于元素去重(如:列表去重);
    set 是无序的,支持并、交、差及对称差等数学运算, 但由于 set 不记录元素位置,因此不支持索引、分片等类序列的操作。
    set 不能嵌套,不能存可以改变的数据类型,如列表,字典;可以存数字、字符串、元组;
    set 和 dict 的唯一区别仅在于没有存储对应的 value,但是,set 的原理和 dict 一样,所以,同样不可以放入可变对象,因为无法判断两个可变对象是否相等,也就无法保证 set 内部“不会有重复元素”。

 集合的特点也就是我们接下来要对集合进行基本操作的基础。
 二、集合的基本操作
1、创建集合

    #创建一个空集合时,只能使用s=set(),因为s={}创建的是空字典
    set0 = set()  
    #创建一个数值集合,由此我们也可以看出集合可自动去重
    set1 = set([1,2,3,4])                                 #故set不能存列表,会将其自动转化为set
    set2 = {1,2,3,4}
    #创建一个字符集合
    set3= set("HelloWorld!")
    #set和dict一样,只是没有value,相当于dict的key集合。
    set4 = set({'name':'zhangsan','age':20,'weigh':50})   #故set不能存字典,会将其自动转化为set
     
    print('set0:',set0,type(set0))
    print('set1:',set1,type(set1))
    print('set2:',set2,type(set2))
    print('set3:',set3,type(set3))
    print('set4:',set4,type(set4))
     
    运算结果:
    set0: set() <class 'set'>
    set1: {1, 2, 3, 4} <class 'set'>
    set2: {1, 2, 3, 4} <class 'set'>
    set3: {'o', 'r', 'W', 'd', '!', 'H', 'e', 'l'} <class 'set'>   #打印的结果是去重后的结果
    set4: {'weigh', 'name', 'age'} <class 'set'>

2、去重

也可实现去除掉列表中的重复元素

    #集合去重
    set1={1,2,3,4,5,2}
    print("set1去重后:",set1)
    #列表去重
    list1 = [3,6,9,4,5,3,5,6,7]
    new_list = list(set(list1))
    print("list1去重后:",new_list)
     
    运行结果:
    set1去重后: {1, 2, 3, 4, 5}
    list1去重后: [3, 4, 5, 6, 7, 9]

3、集合元素的增、删以及判断元素是否在集合中存在

set 是无序的,  不记录元素的位置,因此不支持索引、分片等类序列的操作。

3.1> 增加元素

set.add(x):将元素 x 添加到集合 set 中,如果元素已存在,则不进行任何操作。

    set1 = {"Google", "Runoob", "Taobao"}
    set1.add("Facebook")
    print(set1)
     
    运行结果:
    {'Facebook', 'Google', 'Taobao', 'Runoob'}

set.update(x)参数可以是列表,元组,字典等;x 可以有多个,用逗号分开

    set1 = {"Google", "Runoob", "Taobao"}
    set1.update((9,0))
    print(set1)
    set1.update({1,3})
    print(set1)
    set1.update([1,4],[5,6])  
    print(set1)
     
    运行结果:
    {0, 'Runoob', 9, 'Taobao', 'Google'}
    {0, 1, 3, 'Runoob', 9, 'Taobao', 'Google'}
    {0, 1, 3, 4, 5, 'Runoob', 6, 9, 'Taobao', 'Google'}

3.2> 删除元素

set.remove(x):将元素 x 从集合 s 中移除,如果元素不存在,则会发生错误。

    set1 = {"Google", "Runoob", "Taobao"}
    set1.remove("Taobao")
    print(set1)
    print('-------------------------------')
    set1.remove("Facebook")   # 不存在会发生错误
     
    运行结果:
    {'Google', 'Runoob'}
    -------------------------------
    Traceback (most recent call last):
      File "D:\st13\python\1.14\lx.py", line 5, in <module>
        set1.remove("Facebook")   # 不存在会发生错误
    KeyError: 'Facebook'

set.discard(x):移除集合中的元素,且如果元素不存在,不会发生错误。

    set1 = {"Google", "Runoob", "Taobao"}
    set1.discard("Runoob")
    print(set1)
    print('---------------------')
    set1.discard("Facebook")  #不存在不会发生错误
    print(set1)
     
    运行结果:
    {'Google', 'Taobao'}
    ---------------------
    {'Google', 'Taobao'}

set.pop():随机删除集合中的一个元素,多次执行测试结果都不一样。

    set1 = {"Google", "Runoob", "Taobao", "Facebook"}
    x = set1.pop()
    print(x)       #随机删除了'Google'
    print(set1)
     
    运行结果:
    Google
    {'Taobao', 'Facebook', 'Runoob'}

然而在交互模式(cmd命令下),pop 是删除集合的第一个元素(排序后的集合的第一个元素)。

set.clear():清空集合

    set1 = {"Google", "Runoob", "Taobao"}
    set1.clear()
    print(set1)
     
    运行结果:
    set()

4、判断元素是否在集合中存在

x in set:判断元素 x 是否在集合 s 中,存在返回 True,不存在返回 False。

    set1 = {"Google", "Runoob", "Taobao"}
    print("Runoob" in set1)
    print("Facebook" in set1)
     
    运行结果:
    True
    False

5、计算集合元素个数

len(set):计算集合 s 元素个数。

    set1 = {"Google", "Runoob", "Taobao"}
    print(len(set1))
     
    运行结果:
    3

三、集合的运算操作

即集合的最基本操作,如集合取交集(&)、并集(|)、取差集、判断一个集合是不是另一个集合的子集或者父集等。

    a={1,3,4,2,5}
    b={1,2,3,6,7}

1、取交集:& / intersection()

    set1 = {1,3,4,2,5}
    set2 = {1,2,3,6,7}
    a = set1 & set2
    print(a)
    b = set1.intersection(set2)
    print(b)
     
    运行结果:
    {1, 2, 3}
    {1, 2, 3}

2、取并集:| / union()

    set1 = {1,3,4,2,5}
    set2 = {1,2,3,6,7}
    a = set1 | set2
    print(a)
    b = set1.union(set2)
    print(b)
     
    运行结果:
    {1, 2, 3, 4, 5, 6, 7}
    {1, 2, 3, 4, 5, 6, 7}

3、异或:^ / symmertric_difference(),对称差集,即去掉两个集合的共同部分

    set1 = {1,3,4,2,5}
    set2 = {1,2,3,6,7}
    a = set1 ^ set2
    print(a)   
    b = set1.symmetric_difference(set2)
    print(b)
     
    运行结果:
    {4, 5, 6, 7}
    {4, 5, 6, 7}

4、差集:- / difference()

    set1 = {1,3,4,2,5}
    set2 = {1,2,3,6,7}
    a = set1 - set2      #set1减去set1与set2的交集
    print(a)  
    b=set1.difference(set2)
    print(b)
     
    运行结果:
    {4, 5}
    {4, 5}

5、子集:issubset(子集)/issuperset(父集),返回的是布尔值

    set1 = {1,2,3,4,5}
    set2 = {1,3,5}
    a =set2.issubset(set1)   #set2是set1的子集
    print(a)
    b=set1.issuperset(set2)  #set1是set2的父集
    print(b)
     
    运行结果:
    True
    True

面试常问问题:

  1  list列表可以存储的类型结构:数字,字符串,元组,列表,字典,set

  2  tuple元组可以存储数据类型:数字,字符,元组,列表,字典,set

  3  dict字典可以存储数据类型:分别说:键(不可变),值(数字,字符,元组,列表,字典,set集合)

  4  set集合可以存储数据类型:不可变类型(数字,字符,元组)

        5 list是可变对象,tuple保存的引用不可变

        6 数组和列表不同点:1,数组元素类型要相同,而Python列表不需要 2,数组长度固定,而Python列表可以增加

七、栈

栈(Stack)是一个数据集合,可以理解为只能在一端进行插入或删除操作的列表。

栈的特点:后进先出 LIFO(Last-in, First-out)

栈的概念有:栈顶,栈底

栈的基本操作:

  1. 进栈(压栈):push
  2. 出栈:pop
  3. 取栈顶:gettop

栈的实现:

class Stack(object):
    def __init__(self, limit=10):
        self.stack = [] #存放元素
        self.limit = limit #栈容量极限
    def push(self, data): #判断栈是否溢出
        if len(self.stack) >= self.limit:
            print('StackOverflowError')
            pass
        self.stack.append(data)
    def pop(self):
        if self.stack:
            return self.stack.pop()
        else:
            raise IndexError('pop from an empty stack') #空栈不能被弹出
    def peek(self): #查看堆栈的最上面的元素
        if self.stack:
            return self.stack[-1]
    def is_empty(self): #判断栈是否为空
        return not bool(self.stack)
    def size(self): #返回栈的大小
        return len(self.stack)

栈的链表实现


class Node(object):

"""节点的实现"""
    def __init__(self,elem):
         self.elem = elem
         self.next = None

class Stack(object):

    def __init__(self):
    """ 初始化,链表头head"""
        self.__head = None

    def is_empty(self):

    """ 初始化,链表头head"""
        return  self.__head is None

    def push(self,item):

    """ 压栈"""
        node = Node(item)
        node.next = self.__head
        self.__head = node

    def pop(self):
    """ 弹栈"""
        if self.is_empty():
            return 
        else:
            p = self.__head
            self.__head = p.next
            return p.elem

通过栈来解决括号匹配的问题,就是解决IDE实现括号未匹配成功报错的问题。

class Stack:
 
    def __init__(self):
        self.stack = []
 
    def push(self,element):
        return self.stack.append(element)
 
    def pop(self):
        return self.stack.pop()
 
    def get_top(self):
        if len(self.stack) > 0:
            return self.stack[-1]
        else:
            return None
 
    def is_empty(self):
        return len(self.stack) == 0
 
def brace_match(s):
    match = {'}': '{', ']': '[', ')': '('}
    stack = Stack()
    for ch in s:
        if ch in {'(', '[', '{'}:
            stack.push(ch)
        else:
            if stack.is_empty():
                return False
            elif stack.get_top() == match[ch]:
                stack.pop()
            else:
                return False
 
    if stack.is_empty():
        return True
    else:
        return False
 
print(brace_match('{[{()}]}'))

八、队列

队列(Queue)是一个数据集合,仅允许在列表的一端进行插入,另一端进行删除。进行插入的一端称为队尾(rear),插入动作称为进队或者入队。进行删除的一端称为对头(front),删除动作称为出队。

队列的性质:先进先出(First-in ,  First-out)

数组形式实现队列:

class Queue():
    def __init__(self):
        self.entries = [] #表示队列内的参数
        self.length = 0 #表示队列的长度
        self.front=0 #表示队列头部位置

    def enqueue(self, item):
        self.entries.append(item) #添加元素到队列里面
        self.length = self.length + 1 #队列长度增加 1

    def dequeue(self):
        self.length = self.length - 1 #队列的长度减少 1
        dequeued = self.entries[self.front] #队首元素为dequeued
        self.front+=1 #队首的位置减少1
        self.entries = self.entries[self.front:] #队列的元素更新为退队之后的队列
        return dequeued

    def peek(self):
        return self.entries[0] #直接返回队列的队首元素

链表形式实现队列

class Node(object):
    def __init__(self,elem,next=None):
        self.elem = elem #表示对应的元素值
        self.next=next #表示下一个链接的链点

class Queue(object):
    def __init__(self):
        self.head = None #头部链点为 None
        self.rear = None #尾部链点为 None

    def is_empty(self):
        return self.head is None #判断队列是否为空

    def enqueue(self, elem):
        p = Node(elem) #初始化一个新的点
        if self.is_empty():
            self.head = p #队列头部为新的链点
            self.rear = p #队列尾部为新的链点
        else:
            self.rear.next = p #队列尾部的后继是这个新的点
            self.rear =p #然后让队列尾部指针指向这个新的点

    def dequeue(self):
        if self.is_empty(): #判断队列是否为空
            print('Queue_is_empty') #若队列为空,则退出 dequeue 操作
        else:
            result = self.head.elem #result为队列头部元素
            self.head = self.head.next #改变队列头部指针位置
            return result #返回队列头部元素

    def peek(self):
        if self.is_empty(): #判断队列是否为空
            print('NOT_FOUND') #为空则返回 NOT_FOUND
        else:
            return self.head.elem #返回队列头部元素

    def print_queue(self):
        print("queue:")
        temp=self.head
        myqueue=[] #暂时存放队列数据
        while temp is not None:
            myqueue.append(temp.elem)
            temp=temp.next
        print(myqueue)

九、链表

链表实际上是线性表的链式存储结构,与数组不同的是,它是用一组任意的存储单元来存储线性表中的数据,存储单元不一定是连续的,且链表的长度不是固定的,链表数据的这一特点使其可以非常的方便地实现节点的插入和删除操作。

当我们存储一大波数据时,我们很多时候是使用数组,但是当我们执行插入操作的时候就是非常麻烦,比如,有一堆数据1,2,3,5,6,7 我们要在3和5之间插入4,如果用数组,我们会怎么办?当然是将5之后的数据往后退一位,然后再插入4,这样非常麻烦,但是如果用链表,就直接在3和5之间插入4即可。

 所以链表的节点结构如下:

data为自定义的数据,next为下一个节点的地址。head保存首位节点的地址。

链表与数组比较

数组(包括结构体数组)的实质是一种线性表的顺序表示方式,它的优点是使用直观,便于快速、随机地存取线性表中的任一元素,但缺点是对其进行 插入和删除操作时需要移动大量的数组元素,同时由于数组属于静态内存分配,定义数组时必须指定数组的长度,程序一旦运行,其长度就不能再改变,实际使用个数不能超过数组元素最大长度的限制,否则就会发生下标越界的错误,低于最大长度时又会造成系统资源的浪费,因此空间效率差

单链表:

class Node:  
    def __init__(self, data):
        self.data = data  
        self.next = None 
 
class Linked_List:
    def __init__(self):
        self.head = None

    def initlist(self,data_list):    #链表初始化函数  尾插法
        self.head=Node(data_list[0])   #创建头结点 有头结点且头结点存在数据
        temp=self.head
        for i in data_list[1:]: #逐个为 data 内的数据创建结点, 建立链表
            node=Node(i)
            temp.next=node
            temp=temp.next

    def is_empty(self):  #判断链表是否为空
        if self.head.next==None:
            print("Linked_list is empty")
            return True
        else:
            return False

    def get_length(self):  #获取链表的长度
        temp=self.head #临时变量指向队列头部
        length=0 #计算链表的长度变量
        while temp!=None:
            length=length+1
            temp=temp.next
        return length #返回链表的长度

    def insert(self,key,value): #链表插入数据函数
        if key<0 or key>self.get_length()-1:
            print("insert error")
        temp=self.head
        i=0
        while i<=key: #遍历找到索引值为 key 的结点后, 在其后面插入结点
            pre=temp
            temp=temp.next
            i=i+1
        node=Node(value)
        pre.next=node
        node.next=temp

    def print_list(self):   #遍历链表,并将元素依次打印出来
        print("linked_list:")
        temp=self.head
        new_list=[]
        while temp is not None:
            new_list.append(temp.data)
            temp=temp.next
        print(new_list)

    def remove(self,key):  #链表删除数据函数  头结点索引值为0
        if key<0 or key>self.get_length()-1:
            print("insert error")
        i=0
        temp=self.head
        while temp !=None:  #遍历找到索引值为 key 的结点
            pre=temp
            temp=temp.next
            i=i+1
            if i==key:
                pre.next=temp.next
                temp=None
                return True
        pre.next=None

    def reverse(self): #将链表反转
        prev = None
        current = self.head
        while current:
            next_node = current.next
            current.next = prev
            prev = current
            current = next_node
        self.head = prev

双向链表

class Node(object):
    # 双向链表节点
    def __init__(self, item):
        self.item = item
        self.next = None
        self.prev = None

class DLinkList(object):
    # 双向链表
    def __init__(self):
        self._head = None

    def is_empty(self):    #头结点存在数据,对应的索引为0
        # 判断链表是否为空
        return self._head == None

    def get_length(self):
        # 返回链表的长度
        cur = self._head
        count = 0
        while cur != None:
            count=count+1
            cur = cur.next
        return count

    def travel(self):
        # 遍历链表
        cur = self._head
        while cur != None:
            print(cur.item)
            cur = cur.next
        print("")

    def add(self, item):
        # 头部插入元素
        node = Node(item)
        if self.is_empty():
            # 如果是空链表,将_head指向node
            self._head = node
        else:
            # 将node的next指向_head的头节点
            node.next = self._head
            # 将_head的头节点的prev指向node
            self._head.prev = node
            # 将_head 指向node
            self._head = node

    def append(self, item):
        # 尾部插入元素
        node = Node(item)
        if self.is_empty():
            # 如果是空链表,将_head指向node
            self._head = node
        else:
            # 移动到链表尾部
            cur = self._head
            while cur.next != None:
                cur = cur.next
            # 将尾节点cur的next指向node
            cur.next = node
            # 将node的prev指向cur
            node.prev = cur

    def search(self, item):
        # 查找元素是否存在
        cur = self._head
        while cur != None:
            if cur.item == item:
                return True
            cur = cur.next
        return False

    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
            count = 0
            # 移动到指定位置的前一个位置
            while count < (pos-1):
                count += 1
                cur = cur.next
            # 将node的prev指向cur
            node.prev = cur
            # 将node的next指向cur的下一个节点
            node.next = cur.next
            # 将cur的下一个节点的prev指向node
            cur.next.prev = node
            # 将cur的next指向node
            cur.next = node

    def remove(self, item):
        # 删除元素
        if self.is_empty():
            return
        else:
            cur = self._head
            if cur.item == item:
                # 如果首节点的元素即是要删除的元素
                if cur.next == None:
                    # 如果链表只有这一个节点
                    self._head = None
                else:
                    # 将第二个节点的prev设置为None
                    cur.next.prev = None
                    # 将_head指向第二个节点
                    self._head = cur.next
                return
            while cur != None:
                if cur.item == item:
                    # 将cur的前一个节点的next指向cur的后一个节点
                    cur.prev.next = cur.next
                    # 将cur的后一个节点的prev指向cur的前一个节点
                    cur.next.prev = cur.prev
                    break
                cur = cur.next

十、哈希表

众所周知,HashMap是一个用于存储 Key-Value键值对的集合,每一个键值对也叫Entry,这些键值对(Entry)分散存储在一个数组当中,这个数组就是HashMap的主干。

  使用哈希表可以进行非常快速的查找操作,查找时间为常数,同时不需要元素排列有序;Python的内建数据类型:字典就是用哈希表实现的。

  Python中的这些东西都是哈希原理:字典(dictionary)、集合(set)、计数器(counter)、默认字典Defaut dict)、有序字典(Order dict).

  哈希表示一个高效的查找的数据结构,哈希表一个通过哈希函数来计算数据存储位置的数据结构,通常支持如下操作:

  • insert(key, value):插入键值对(key, value)
  • get(key):如果存在键为key的键值对则返回其value,否则返回空值
  • delete(key):删除键为key的键值对

直接寻址表

直接寻址技术缺点:

  1. 当域U很大时,需要消耗大量内存,很不实际
  2. 如果域U很大而实际出现的key很少,则大量空间被浪费
  3. 无法处理关键字不是数字的情况

哈希

  直接寻址表:key为k的元素放到k位置上

  改进直接寻址表:哈希(Hashing)

  • 构建大小为m的寻址表T
  • key为k的元素放到 h(k)位置上
  • h(k) 是一个函数,其将域U映射到表 T[0, 1, ... , m-1]

哈希表

  哈希表(Hash Table,又称为散列表),是一种线性表的存储结构。哈希表由一个直接寻址表和一个哈希函数组成。哈希函数h(k)将元素关键字 k 作为自变量,返回元素的存储下标。

  假设有一个长度为7的哈希表,哈希函数 h(k)=k%7.元素集合 {14, 22, 3, 5}的存储方式如下图:

 

7.1  哈希表的应用

  1,集合与字典

  字典与集合都是通过哈希表来实现的。比如:

1

dic_res = {'name':'james', 'age':32, 'gender': 'Man'}

  使用哈希表存储字典,通过哈希表将字典的键映射为下标,假设 h('name')=3,h('age')=4,则哈希表存储为:

1

[None, 32, None, 'james', 'Man']

  如果发生哈希冲突,则通过拉链法或开发寻址法解决。

  2,md5算法

  MD5(Message-Digest Algorithm 5)曾经是密码学中常用的哈希函数,可以把任意长度的数据映射为128位的哈希值,其曾经包含如下特征:

  • 1,同样的消息,其MD5值必定相同;
  • 2,可以快速计算出任意给定消息的MD5值;
  • 3,除非暴力的枚举所有可能的消息,否则不可能从哈希值反推出消息本身;
  • 4,两天消息之间即使只有微小的差别,其对应的MD5值也应该是完全不同,完全不相关的;
  • 5,不能再有意义的时间内人工的构造两个不同的消息,使其具有相同的MD5值

  3,md5

  应用举例:文件的哈希值

  算出文件的哈希值,若两个文件的哈希值相同,则可认为这两个文件时相同的,因此:

  • 1,用户可以利用它来验证下载的文件是否完整
  • 2,云存储服务商可以利用它来判断用户要上次的文件是否已经存在于服务器上,从而实现秒传的功能,同时避免存储过多相同的文件副本。

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值