数据结构----读书笔记二(线性表的知识点)

在大话数据结构中,之后的内容应该最好是编程实现会比较有感触,所以,在之后的学习中,书本是用C写的,我会用Python再重新书写一遍,以便更好的记忆和学习。

2018.5.22重新思考
1,头节点与头指针的关系:
头节点是为了使得不管在任何地方进行插入删除的操作是统一的。
头指针指向链表的头节点,代表链表。
2,链表为空的时候,头指针指向头结点,头结点指向NULL,此时代表链表为空。
3,头插法和尾插法
头插法,实现的链表数据与输入数据是相反的。
尾插法,实现的链表数据与输入数据是相同的。需要为尾指针来记录。
4,用空间换时间的典型代表,线性表的链式存储方式与线性表的顺序存储方式;双向链表与单链表。

第三章,线性表的内容
线性表指的是逻辑结构是线性的情况,逻辑结构是线性的情况中,说明数据元素之间具有有序性以及有限性,也就是数据元素有限个,并且数据元素之间的排列是线性的,也就是排列的规律性是依次的情况,或者可以说具有某种一一对应的关系?哈哈。
对于逻辑结构为线性的数据结构,物理结构有二种,一种是顺序存储的物理结构,也就是在计算机存储中也是采用了一片连续的空间来进行存储,在这种情况下需要设定需要的内存空间,需要知道现在线性表中元素的个数,以及第一个元素的存储的位置;另外一种是链式存储的物理结构,在这种情况下,我们不需要连续的存储空间,但我们需要记录我们的下一个数据存储在哪里。二者各有优缺点,下面一一的介绍。

顺序存储结构的线性表的python实现

# -*- coding:utf-8 -*-
class seq_L(object):

     # 初始化线性表,也就是创建线性表,在这里利用list列表来实现,且可以不限定其长度,长度可以动态变化
     def __init__(self):
          self._list = []
          self._length = 0

     # 得到某个位置的元素值
     def get_locData(self, loc):
          if (loc < 0) or (loc >= len(self._list)):
               print "Error to get data!"
               return False
          else:
               print "the %s data is %s" %(loc, self._list[loc])
               return self._list[loc]

     # 在某个位置插入元素,若超过现有线性表的长度则插入线性表的最后
     def insert_locData(self, data, loc):
          if loc < 0:
               print "Error Input"
               return False
          elif loc >= len(self._list):
               print "Insert in The final of the list"
               self._list.append(data)
               self._length += 1
          else:
               self._list.insert(loc, data)
               self._length += 1

     # 删除某个位置的元素
     def remove_locData(self, loc):
          if (loc < 0) or (loc >= len(self._list)):
               print "Error Input"
               return False           
          else:
               del self._list[loc]
               self._length -= 1

     # 判断某个元素是否在线性表中
     def is_data_in(self, data):
          if data in self._list:
               print "the data is in the seqL"
          else:
               print "the data is not in the seqL"

     # 线性表的长度
     def get_length(self):
          print "the length of seqL is %s" %(self._length) 

if __name__ == "__main__":
     seqL = seq_L()
     seqL.get_length()
     seqL.insert_locData(1, 0)
     seqL.insert_locData(2, 0)
     seqL.is_data_in(2)
     seqL.remove_locData(0)
     print seqL._list

利用python,简单的实现了顺序存储的线性表,最终的运行结果如下:

>>> 
the length of seqL is 0
Insert in The final of the list
the data is in the seqL
[1]
>>> 

对比Python版本和C语言版本,寻找不同点。
Python版本中,提供的一些基本的数据结构有,列表,元组以及字典,在这边的话我们采用的是列表,因为显然,字典的结构在这边不适合,而且元组中的元素是不可改变的,所以对于插入删除操作比较困难,故采用的是列表的方式。列表是可以动态的改变的,所以我们只要记录线性表中的列表,以及线性表现有元素的多少,即其长度。然后定义了一些操作,要考虑一下极端的情况,但其实程序还是不够完整和完美,只是初步的实现而已,因为其并没有考虑全非法的情况,以及没有将常用的操作全部列举完,但whatever,可以用目前是王道~~
对于C语言版本而言,采用的是数组的实现方式,但C语言中的数组是需要提前指定大小的,需要提前预留空间的,所以这就有一个限制条件,不能不够用还不能太浪费,太考验人的能力了,除了要记录第一个数据元素存储的单元,数据元素的个数还需要知道分配给线性表的存储空间的大小。

单链表结构,存储的空间是不需要连续的方式,但需要在每个节点处除了保存数据外,还需要额外的多加一个存储下一个数据的指针,需要额外的空间来换取频繁的进行插入,删除时候的开销问题。
具体的Python实现版本如下:

# -*- coding:utf-8 -*-

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

class LKList(object):
     def __init__(self):
          self.L = Node(None)
          self.L.next = None
          self.length = 0

     def is_empty(self):
          return self.length == 0

     def get_length(self):
          return self.length

     def insert(self, i, elem):
          j = 0
          p = self.L
          while j < i-1 and p is not None:
               j += 1
               p = p.next
          if p is None:
               raise IndexError("Index is out of range!")
          else:
               tmp = Node(elem)
               tmp.next = p.next
               p.next = tmp
               self.length += 1

     def delete(self, i):
          if self.is_empty():
               raise IndexError("The list is empty!")
          elif 0 < i <= self.length:
               j = 1
               p = self.L
               while j < i and p:
                    p = p.next
                    j += 1
               delete_node = p.next
               p.next = delete_node.next
               self.length -= 1
               return delete_node.data
          else:
               raise IndexError("Index is out of range!")

     def get_elem(self, i):
          if self.is_empty():
               raise IndexError("The list is empty")
          elif 0 < i <= self.length:
               j = 0
               p = self.L
               while j < i and p:
                    p = p.next
                    j += 1
               print p.data
          else:
               raise IndexError("Index is out of range!")

     def locate_elem(self, elem):
          j = 0
          p = self.L
          while p is not None and p.data != elem:
               p = p.next
               j += 1
          if p is Node:
               return -1
          else:
               return j

     def create_dict_list_H(self, list):
          p = self.L
          for i in range(len(list)):
               tmp = Node(list[i])
               tmp.next = p.next
               p.next = tmp
               self.length += 1

     def create_dict_list_E(self, list):
          p = self.L
          r = p
          for i in range(len(list)):
               tmp = Node(list[i])
               r.next = tmp
               r = tmp
               self.length += 1
          r.next = None

     def show_lklist(self):
          if self.is_empty():
               raise IndexError("It is a empty list!")
          else:
               j = 1
               p = self.L
               while j <= self.length and p:
                    p = p.next
                    if p is not None:
                         print p.data
                    j += 1

if __name__ == "__main__":
     lk = LKList()
     lk.create_dict_list_E([1,2,3,4])
     print "------"
     lk.get_elem(1)

关键的点在于:python中没有指针和地址的概念,所以比较难的构建在于如何来得到下一个节点。
上述的写法,参考了网上大神的写法,在程序中,我们定义了二种结构,一种是对于节点的定义,一种是对于链表的定义,在对于链表的初始化中,我们初始化了一个头结点,然后对于创建的新节点而言,数值部分有实际的意义,而对于下一个节点的存储的意义是不存在的,故只要设置为空即可,所以有一个统一的点结构,如果有后续的节点 只要再设置后续的节点即可。
只有动手,才知道有多难,或是有多容易,加油吧·~

静态链表(游标实现法),我们一般是通过数组来实现,每一个数组元素有二个域,一个是数据域data域,一个是游标域(cur),类似于指针域。
对于静态链表而言,我们对于数组的第一个和最后一个数组的元素作为特殊的处理。
具体对于静态链表,我的理解如下:(c或者c++实现版本)
1,需要开辟一块空间,为了更好的插入操作,所以,这一块空间按理来说应该开辟的比较大~·
2, 初始化静态链表,最应该进行的初始化指定的部分是游标域的那个部分。所以,依次指向后面的一个数组的下标,最后一个指向第一个。
3,静态链表的插入操作和删除操作。
数组的第一个元素用于存放空闲的备用链表的下标,所以通过这个特性来构建一个空闲的备用链表,来实现malloc()和free()的功能。
4,静态链表的其他操作,比如统计链表长度等操作。
可以通过最后一个元素存储的是有效的数据的下标的特性,然后有效数据的最后一个数组的游标值为第一个数组,即0,所以可以通过这个来得到静态链表的长度的操作。
静态链表的优点:插入删除操作的时候比较方便,不需要移动大量的数据,只需要修改游标即可。
静态链表的缺点:之前确定不了到底要开辟多大的一个连续空间,而且失去了线性表中线性存储的优点。

循环链表
在单链表中,到了链表的最后会把原有的信息失去了,所以为了从不管哪个数据开始都可以遍历整个链表的数据,所以,我们需要这样的一个结构,将所有的元素弄成一个圈,均可以绕所有的数据一周。
第一种方法是通过链表结尾的指针域来指向头节点,这样的思路是顺畅的,但对于最后一个元素的访问是低效的。
第二种方法是通过尾指针来实现,可以在某种程度上改进这种低效,并且在合并链表的时候会方便的比较多。在合并链表时需要注意:1,注意对于节点的备份,以免丢失信息;2,注意对于不用的头结点的空间的释放问题~~

双向链表
双向链表,维护二个指针,前驱指针以及后继指针,以及其数据三个部分。对于双向链表而言,一些操作比如查找元素,获得长度等操作,还是只需要一个方向的指针,二个方向并没有什么用;因为加入前驱和后继,所以在维护的时候需要特别注意指定的顺序就好。
双向链表的插入:
首先最后修改的是p->next这个东西,记住这个大原则就应该没有问题噶·~
顺序:需要先把插入节点的前驱节点,以及后继节点搞定;然后再搞定后续节点的前驱的问题;最后再搞定前驱节点的后续节点的问题~~
双向链表的删除:
注意删除后要释放那个节点的存储空间。
双向链表的缺点在于可能在插入和删除操作的时候,需要额外的注意,但因为保存了二个方向的指针,所以在对于某个结点的前后结点进行操作的时候比较方便,利用空间换时间的典型代表。

你可能感兴趣的:(数据结构_操作系统_数学的学习)