线性表
1.定义:
零个或多个数据元素的有限序列。
【线性表】:某类元素的集合并且记录着元素之间一种顺序关系,是最基本的数据结构之一,例如:list 和 tuple
2.表现形式
2.1 顺序表示(顺序表)
概念:用一组地址连续的存储单元依次存储线性表的数据元素,这种存储结构的线性表称为顺序表。
特点:逻辑上相邻的数据元素,物理次序也是相邻的。
只要确定好了存储线性表的起始位置,线性表中任一数据元素都可以随机存取,所以线性表的顺序存储结构是一种随机存取的储存结构,因为高级语言中的数组类型也是有随机存取的特性,所以通常我们都使用数组来描述数据结构中的顺序储存结构,用动态分配的一维数组表示线性表。
2.2链表
概念:用一组任意的存储单元存储线性表的数据元素(这组存储单元可以是连续的,也可以是不连续的),包括数据域和指针域,数据域存数据,指针域指示其后继的信息。
结点
结点分为数据域和指针域,数据域保存着作为表元素的数据项,指针域保存同一个表里的下一个结点的标识(太绕了,通俗理解就是表明这个结点指向谁)。
-
头结点(头结点不一定是第一个结点) 放在第一个结点之前,它的数据域一般没有意义,并且它本身也不是链表必须要带的。
-
设立头结点的目的是什么呢?
为了在某些时候可以更方便的对链表进行操作,有了头结点,我们在对第一个元素前插入或者删除结点的时候,它的操作与其它结点的操作就统一了。 -
头指针 指向链表第一个结点的指针,它是链表的必备元素且无论链表是否为空,头指针都不能为空,有头结点的话,那么就是指向头结点的指针。
-
设立头指针的目的是什么呢?
访问链表的时候我们需要知道它的位置,才能通过它的指针域找到下一个结点的位置,也就是说知道了头指针,整个链表的元素我们都是可以访问的,所以它必须要存在,这也就是我们常说的链表的「标识」,这也就是为什么我们一般用头指针来表示链表。
单链表如图:
其实单链表可以想象成一列人在玩游戏,每个人都把手搭到后面那个人的肩膀上,每个人身上都有一个大口袋用来放数据,最后一个人没人可以搭就一直悬空着,第一个带头领队的就不用口袋了,它是一个头结点,是用来找到第一个有口袋的人的,也就是首元结点。
单链表基本操作原理
这样想的话就简单了,初始化的时候就是用一个人当头结点,它没有口袋,他的手是用来搭到第一个有口袋的人肩膀的,因为这个人还没来,所以它的next是Null,而取值时,通过参数i,我们就可以从首元结点开始数,数到第i个人,找到他后,就可以拿他口袋里面的东西,查找是知道口袋里面东西是什么,想找到这个东西的拥有者,也是一样从首元结点开始找。遍历下去,插入的话,比如要插入第i个位置,那么我们就先找到第i-1个人,然后让新来的手搭到第i个人身上,然后再让第i-1个人把之前放在第i个人的手挪开,放在新来的人的肩膀上,删除操作的话,例如删除第i个人,那么也是先找到第i-1个人,这里的重点是,因为链表的查询只能是从头开始找的,是不能逆回去的,所以我们需要找个变量把要删的那个人的地址先存起来,然后把第i-1个的手放到第i+1个人身上,如果我们不找个变量把那个人的地址存起来,这时候我们就没办法找到他了,因为我们用一个变量临时保存他的地址,于是我们只需要释放这个地址的空间就可以了。
题目
反转一个单链表。
示例:
输入: 1->2->3->4->5->NULL
输出: 5->4->3->2->1->NULL
进阶:
你可以迭代或递归地反转链表。你能否用两种方法解决这道题?
思路
python
运用三个指针,pre、cur、lat 使用pre和cur两个指针配合工作,使得两个节点间的指向反向,依次循环
pre代表null
cur代表head(头结点)
lat代表为下一个节点
pre cur lat
null 1 -> 2 -> 3 -> 4 -> 5 -> null
第一次循环
cur= pre->next
将头结点变成新链表的尾节点
pre cur lat
null <-1 2 -> 3 -> 4 -> 5 -> null
pre = cur,cur = lat,lat = lat.next
pre cur lat
null <-1 2 -> 3 -> 4 -> 5 -> null
依次循环操作直到lat=None
pre cur lat
null <-1 <- 2 <- 3 <- 4 5 -> null
class Solution(object):
def reverseList(self, head):
"""
:type head: ListNode
:rtype: ListNode
"""
if head == None:
return []
pre = None
cur = head
lat = head.next
while lat != None:
cur.next = pre
pre = cur
cur = lat
lat = lat.next
cur.next = pre
return cur
java
首先,我们要知道最后返回的结果是链表尾节点。但先找到尾节点是很难继续实现的,因为链表没有办法高效获取前驱。要想到建立一个新的节点,之后在遍历输入的时候重新组织节点顺序,将节点挂在新节点上。所以高效的做法是在遍历链表的过程中,一个一个的把输入链表的节点放到一个新的链表头部。所以思路就是建立一个新的链表头,每次遍历输入链表的节点都把他放到新链表的头部,这样遍历完成后就获得了反转的链表。详细代码注释见下。
class Solution {
public ListNode reverseList(ListNode head) {
ListNode first = head;
ListNode reverseHead = null; //建立一个新的节点用来存放结果
while (first != null) { //遍历输入链表,开始处理每一个节点
ListNode second = first.next; //先处理第一个节点first,所以需要一个指针来存储first的后继
first.next = reverseHead; //将first放到新链表头节点的头部
reverseHead = first; //移动新链表的头指针,让它始终指向新链表头部
first = second; //继续处理原链表的节点,即之前指针存放的后继,循环往复
}
return reverseHead;
}
}