比如数组适合做随机读取,
链表适合做插入和删除
// 在 c/c++ 中, 数组的长度是类型的一部分
int arr[5] ; // 表示要给长度为 5 的 int 型的数组
-
线性表的主要操作特点是: 在任意的位置插入和删除元素
-
单链表的存储密度小于 1
-
线性表在需要不断删除或者插入操作的情况下使用链表来实现
-
线性表如果采用链式存储结构, 则要求内存中可用的存储单元的地址连续或不连续都可以
-
链式存储结构所占空间分两部分, 一部分存放结点的值, 另一个部分保存表示结点间关系的指针
-
数据在内存中表示时, 物理地址与逻辑地址相同并且是连续的, 称为顺序存储结构
-
单链表中设置头节点的作用是什么? csdn回答挺好的, 总之就是为了操作简单和减少
bug -
在顺序表中插入和删除一个结点平均需要移动 n 个结点, 具体的移动次数取决于插入和删除的位置
-
线性表的定位操作
ListFind(L,x)的功能是: 在线性表中 L 查找是否存在数据元素x, 如果存在返回线性表中的和x相等的第一个元素的需要 (序号从 0 开始), 如果不存在, 返回 -1package main import ( "fmt" ) func ListFind(data[]int , x int ) int { for index, val:=range data { // 如果 当前的值和 x 相等, 直接返回当前的下标 if val == x { return index } } // 如果没有找到, 直接返回 -1 return -1 } func main() { data:= []int{1,2,3,4,5,6,7,8,8,9} x:= 8 fmt.Println("index:",ListFind(data, x)) // example:7 } -
设顺序表 L 中的数据元素有序递增, 编写一个算法, 将数据元素 x 插入到顺序表L 中的适当位置, 以保持该顺序表的有序性
- 首先需要找到对应的插入位置
- 如果对应的位置后面还有数据, 需要将对应位置下的所有数据整体向后移动一位
- 在对应的位置插入元素 x
-
编写算法实现顺序表的就地逆置
-
使用头尾指针的法则逆序
func reverse(data []int ) { i, j:= 0 , len(data) -1 // 这里使用的是头尾指针的方法, 如果头指针和尾指针不相同,交换当前两个的数据元素 for i!= j { data[i],data[j] = data[j],data[i] i++ j-- } }
-
-
使用单链表实现删除链表中元素数据大于或等于 x 的数据
ListDeleteMore(L, x)- 这里的实现需要注意保存好上一个结点的指针, 如果当前的结点的值大于或者等于 x , 就把上一个结点的值指向当前结点的下一个结点, 如果没有自动垃圾回收的还需要释放当前结点
package main import "fmt" type ListNode struct { Val int Next *ListNode } // 这里使用带有头节点的单链表实现 func ListDeleteMore(head *ListNode, val int ) { curr:=head for curr.Next !=nil { // 如果当前的结点不满足条件, 直接把当前的结点的指针域指向下一个结点的指针域 if curr.Next.Val >= val { curr.Next = curr.Next.Next continue } curr = curr.Next } } func main() { // 这里是构造一个链表 node:=&ListNode{Val:0,Next:&ListNode{Val:2,Next:&ListNode{Val:3,Next:&ListNode{Val:5,Next:&ListNode{Val:5,Next:&ListNode{Val:3,Next:&ListNode{Val:4}}}}}}} ListDeleteMore(node,4) for h:=node.Next; h!=nil ; h = h.Next { fmt.Print(h.Val, " ") } } -
超级经典的一个问题, 单链表的就地逆序
package main import "fmt" type ListNode struct { Val int Next *ListNode } func Reverse(head *ListNode) { curr:= head.Next head.Next = nil // for curr !=nil { tmp:= curr curr = curr.Next // 这里是主要实现就是, 将当前的结点插入到 头节点的 next 域 tmp.Next = head.Next head.Next = tmp } } func main() { node:=&ListNode{Val:0,Next:&ListNode{Val:1,Next:&ListNode{Val:2,Next:&ListNode{Val:3,Next:&ListNode{Val:4}}}}} Reverse(node) for h:=node.Next; h!=nil ; h = h.Next { fmt.Print(h.Val , " ") } } -
设带头节点的单链表 L1 和 L2 中分别存放着两个数据元素的集合, 编写算法判断 L1 是否是集合 L2 的子集
package main import "fmt" type ListNode struct { Val int Next *ListNode } func Contains(L1, L2 *ListNode) bool { // 这里的实现使用了两重的循环 for h1:= L1.Next; h1!=nil ; h1 = h1.Next { h2:= L2.Next for ; h2!=nil ; h2 = h2.Next { if h2.Val == h1.Val { break } } // 如果 h2 == nil 表示寻找是否等于 h1.Val 的值的时候, 在 L2 中没有找到 if h2 == nil { return false } } return true } func main() { node:=&ListNode{Val:0,Next:&ListNode{Val:1,Next:&ListNode{Val:2,Next:&ListNode{Val:3,Next:&ListNode{Val:4,Next:&ListNode{Val:7}}}}}} node2:=&ListNode{Val:0,Next:&ListNode{Val:1,Next:&ListNode{Val:2,Next:&ListNode{Val:3,Next:&ListNode{Val:4}}}}} fmt.Println("contains",Contains(node, node2)) }
使用快慢指针
- 判断链表是否有环
- 找到链表的中点 (快指针每次走两步, 慢指针每次走一步, 快指针走完时, 慢指针刚好走到链表的中点)
跳表
在一个普通链表上建立多级索引用于加快搜索的顺序
:1 -> 5 -> 9
:1 -> 3 -> 5 -> 7 -> 9
1 -> 2 -> 3 -> 4 -> 5 -> 6 -> 7 -> 8 -> 9 -> 10