链表
链表属于线性表,链表是一种物理存储结构上非连续、非顺序的存储结构,数据元素的逻辑顺序是通过链表中的指针链接次序实现的。相较于顺序表,相较于顺序表
优点
- 不需要分配连续的内存空间
- 不需要动态扩容
- 在列表中间添加、删除节点不需要整体移动元素
缺点
- 需要额外的内存空间存放指针
- 查询需要依次遍历每个节点进行查找
基本种类
- 单向链表
- 双向链表
- 循环链表
单链表
单链表每个节点的结构具有value节点值和next指针两个属性,我们可以从头指针开始通过next指针查找到链表上的每个元素。
创建单链表的类型结构体
// 单链表结构体
type List struct {
// 单链表第一个节点指针
first *element
// 单链表最后一个节点指针
last *element
// 单链表的长度
size int
}
// 每个节点的结构体
type element struct {
// 节点值
value interface{}
// 下一个节点的指针
next *element
}
创建一个基本的单链表
// New 创建一个单链表
func New(values ...interface{}) *List {
list := &List{}
if len(values) > 0 {
list.Add(values...)
}
return list
}
链表在末尾添加节点(尾插法)
代码实现
// Add 在链表末尾添加一个元素
func (list *List) Add(values ...interface{}) {
// 遍历需要插入到链表的值列表
for _, value := range values {
// 创建一个新的节点,值为要插入的值
newElement := &element{value: value}
// 如果当前链表的大小为0,则链表的头尾指针都指向这个新的节点
if list.size == 0 {
list.first = newElement
list.last = newElement
} else {
// 链表有其他节点的情况下,向链表的末尾添加新的节点
// 1. 将链表的尾指针的next指向新的节点
list.last.next = newElement
// 2. 将这个新的节点作为尾指针
list.last = newElement
}
list.size++
}
}
在链表头部插入新节点(头插法)
func (list *List) Prepend(values ...interface{}) {
// 反向遍历要插入的节点列表,保证插入顺序,比如:["c","d"] -> Prepend(["a","b"]) -> ["a","b","c",d"]
for v := len(values) - 1; v >= 0; v-- {
// 1. 创建一个新的节点,新节点的next指向链表的头结点
newElement := &element{value: values[v], next: list.first}
// 2. 再将新节点作为链表的头结点
list.first = newElement
// 3. 如果是空链表,链表的尾结点也指向新节点
if list.size == 0 {
list.last = newElement
}
list.size++
}
}
获取链表第index个节点
// 索引越界判断
func (list *List) withinRange(index int) bool {
return index >= 0 && index < list.size
}
// Get 获取链表第index个元素值
func (list *List) Get(index int) (interface{}, bool) {
// 如果索引越界
if !list.withinRange(index) {
return nil, false
}
element := list.first
// 遍历 当e等于index,对应的element就是目标节点
// 0 -> 头结点
// 1 -> 第二个节点
// ...
// index -> 第index+1个节点(目标节点)
// 示例:
// 在链表0->1->2->3中 如果index = 2
// 0->头结点 1->第二个节点 2->第三个节点
// 所以目标节点为2
for e := 0; e != index; e, element = e+1, element.next {
}
return element.value, true
}
删除第index个节点
func (list *List) Remove(index int) {
if !list.withinRange(index) {
return
}
if list.size == 1 {
list.Clear()
return
}
// 要删除的节点的前一个节点
var beforeElement *element
// 要删除的节点
element := list.first
for e := 0; e != index; e, element = e+1, element.next {
beforeElement = element
}
// 如果要删除的节点是第一个节点
if element == list.first {
list.first = element.next
}
// 如果要删除的节点是最后一个节点
if element == list.last {
list.last = beforeElement
}
// 如果上一个节点不为空,上一个节点的next指针越过element节点
if beforeElement != nil {
beforeElement.next = element.next
}
element = nil
list.size--
}
在目标位置插入节点列表
// Insert 在目标位置插入节点
func (list *List) Insert(index int, values ...interface{}) {
if !list.withinRange(index) {
if index == list.size {
list.Add(values...)
}
return
}
// 链表长度加上插入节点的个数
list.size += len(values)
// foundElement最终指向要插入节点位置的节点
// beforeElement最终指向要插入节点位置前一个节点
// 3->4->5->6 index=2
var beforeElement *element
foundElement := list.first
for e := 0; e != index; e, foundElement = e+1, foundElement.next {
beforeElement = foundElement
}
// 如果foundElement是头结点位置,可以直接调用list.Prepend(values...),或者下面的另一种实现
if foundElement == list.first {
// 保留旧节点列表
oldNextElement := list.first
// 遍历要插入的节点列表
for i, value := range values {
// 创建一个新节点值为value
newElement := &element{value: value}
if i == 0 {
// 如果newElement是插入列表中的第一个节点,将newElement作为新的头结点
list.first = newElement
} else {
// 不是第一个节点,将新节点插入到beforeElement之后
beforeElement.next = newElement
}
// 更新beforeElement,将这次的newElement作为下一次的beforeElement
beforeElement = newElement
}
// 连接上后面的节点
beforeElement.next = oldNextElement
} else {
// 保留beforeElement后面原有的链表的指针
oldNextElement := beforeElement.next
for _, value := range values {
// 创建新的节点
newElement := &element{value: value}
// beforeElement的next指向新节点
beforeElement.next = newElement
// 将新节点作为下一次的beforeElement
beforeElement = newElement
}
// 拼接原有的链表
beforeElement.next = oldNextElement
}
}
其他函数
// Size 返回链表大小
func (list *List) Size() int {
return list.size
}
// Clear 清空链表
func (list *List) Clear() {
list.size = 0
list.first = nil
list.last = nil
}
// Set 设置目标索引位置的值
func (list *List) Set(index int, value interface{}) {
if !list.withinRange(index) {
// 位置等于链表长度,在链表末尾添加节点元素
if index == list.size {
list.Add(value)
}
return
}
foundElement := list.first
for e := 0; e != index; {
e, foundElement = e+1, foundElement.next
}
foundElement.value = value
}
// String 字符串序列化
func (list *List) String() string {
str := "SinglyLinkedList\n"
values := []string{}
for element := list.first; element != nil; element = element.next {
values = append(values, fmt.Sprintf("%v", element.value))
}
str += strings.Join(values, " -> ")
return str
}
// IndexOf 在链表中查找目标值索引
func (list *List) IndexOf(value interface{}) int {
if list.size == 0 {
return -1
}
for index, element := range list.Values() {
if element == value {
return index
}
}
return -1
}
func (list *List) Empty() bool {
return list.size == 0
}
双向链表
双向链表除了指向下一个节点的指针next,还有一个指向上一个节点的指针prev,这样就可以在链表中前后查找、头尾并行查找。
定义结构体
type List struct {
first *element
last *element
size int
}
type element struct {
value interface{}
prev *element
next *element
}
创建一个基本的双链表
func New(values ...interface{}) *List {
list := &List{}
if len(values) > 0 {
list.Add(values...)
}
return list
}
双向链表末尾添加节点
func (list *List) Add(values ...interface{}) {
for _, value := range values {
// 创建的新节点的prev执行链表的尾结点,后面再改变list.last的指针
newElement := &element{value: value, prev: list.last}
if list.size == 0 {
list.first = newElement
list.last = newElement
} else {
list.last.next = newElement
list.last = newElement
}
list.size++
}
}
双向链表头部插入新的节点列表
func (list *List) Prepend(values ...interface{}) {
for v := len(values) - 1; v >= 0; v-- {
newElement := &element{value: values[v], next: list.first}
if list.size == 0 {
list.first = newElement
list.last = newElement
} else {
list.first.prev = newElement
list.first = newElement
}
list.size++
}
}
双向链表删除第index个节点
func (list *List) Remove(index int) {
if !list.withinRange(index) {
return
}
if list.size == 1 {
list.Clear()
return
}
var element *element
// determine traversal direction, last to first or first to last
if list.size-index < index {
element = list.last
for e := list.size - 1; e != index; e, element = e-1, element.prev {
}
} else {
element = list.first
for e := 0; e != index; e, element = e+1, element.next {
}
}
if element == list.first {
list.first = element.next
}
if element == list.last {
list.last = element.prev
}
if element.prev != nil {
element.prev.next = element.next
}
if element.next != nil {
element.next.prev = element.prev
}
element = nil
list.size--
}
双向链表在目标位置插入节点列表
func (list *List) Insert(index int, values ...interface{}) {
if !list.withinRange(index) {
if index == list.size {
list.Add(values...)
}
return
}
list.size += len(values)
var beforeElement *element
var foundElement *element
if list.size-index < index {
foundElement = list.last
for e := list.size - 1; e != index; e, foundElement = e-1, foundElement.prev {
beforeElement = foundElement.prev
}
} else {
foundElement = list.first
for e := 0; e != index; e, foundElement = e+1, foundElement.next {
beforeElement = foundElement
}
}
if foundElement == list.first {
oldNextElement := list.first
for i, value := range values {
newElement := &element{value: value}
if i == 0 {
list.first = newElement
} else {
newElement.prev = beforeElement
beforeElement.next = newElement
}
beforeElement = newElement
}
oldNextElement.prev = beforeElement
beforeElement.next = oldNextElement
} else {
oldNextElement := beforeElement.next
for _, value := range values {
newElement := &element{value: value}
newElement.prev = beforeElement
beforeElement.next = newElement
beforeElement = newElement
}
oldNextElement.prev = beforeElement
beforeElement.next = oldNextElement
}
}