代码随想录第三天| 203 移除链表元素 707 设计链表 206 反转链表

链表

type Node struct {
	Data any
	Next *Node
}

// 创建一个长度为n的链表
func CreateNode(head *Node, n int) {
	for i := 0; i < n; i++ {
		head.Next = &Node{rand.Intn(1000), nil}
		head = head.Next
	}
}

// 打印链表
func PrintLink(head *Node) {
	cur := head
	for cur.Next != nil {
		fmt.Printf("data: %v, point: %p\n\n", cur.Data, cur)
		cur = cur.Next
	}
}


func main() {
	head := &link.Node{10, nil}
	link.CreateNode(head, 5)
	link.PrintLink(head)
}


//data: 10, point: 0xc000010030
//data: 21, point: 0xc000010048
//data: 405, point: 0xc000010060
//data: 402, point: 0xc000010078
//data: 253, point: 0xc000010090
这里打印的node内存地址看似是连续的,实际上是因为我for循环创建的链表,可能分配的是同一块内存空间,导致连续,但是实际上链表本身节点地址不一定是连续的

203 移除链表指定元素

func removeElements(head *ListNode, val int) *ListNode {
	// 思考,虚拟头节点,防止出现要删除第一个节点的情况
	newhead := &ListNode{0, head} // 创建一个头节点,指向题目头节点
	cur := newhead

	for cur.Next != nil {
		temp := cur.Next
		if temp.Val == val {
			// 移除元素,o1
			cur.Next = temp.Next // 指向下一个节点
		}else {
			cur = cur.Next
		}
	}
	return newhead.Next
}
时间 平均遍历n/2 n  空间 n
func removeElements(head *ListNode, val int) *ListNode {
	// 思考  尝试不使用虚拟节点,暴力破解
	if head == nil {
		return head
	}
	// 针对头节点单独处理
	for head.Val == val {
		head = head.Next
		if head == nil {
			return head
		}
	}

	fmt.Println("----------", head)
	cur := head
	for cur != nil && cur.Next != nil {
		if cur.Next.Val == val{  // 中间节点处理
			cur.Next = cur.Next.Next   // 指向下一个节点
		}else {
			cur = cur.Next
		}
	}
	return head
}
时间 n 空间 n

707 设计链表


// !!!!!!!!!!!!!!!!!!!! 下面回答是错误的 !!!!!!!!!!!!!!!!!!

type MyLinkedList struct {
	Val int // 值
	Next *MyLinkedList
	Prev *MyLinkedList
}


func Constructor() MyLinkedList {
	return MyLinkedList{}
}


func (this *MyLinkedList) Get(index int) int {
	if index < 0 {
		return -1
	}
	var count int
	cur := this
	for cur != nil {
		if count == index {
			return cur.Val
		}
		cur = cur.Next
		count++
	}
	return -1 // 全部便利还没找到,说明idx超过链表长度
}


func (this *MyLinkedList) AddAtHead(val int)  {
	var node = &MyLinkedList{val, this, nil}
	this.Prev = node
	return
}


func (this *MyLinkedList) AddAtTail(val int)  {
	var node = &MyLinkedList{val, nil, nil}
	cur := this
	for cur != nil {
		if cur.Next == nil {
			cur.Next = node
			node.Prev = cur
			break
		}else{
			cur = cur.Next
		}
	}
	return
}


func (this *MyLinkedList) AddAtIndex(index int, val int)  {
	var dummyHead = &MyLinkedList{0, this, nil}
	this.Prev = dummyHead

	var node = &MyLinkedList{val, nil, nil}
	var count int
	cur := this
	for cur != nil {
		if count == index {
			temp := cur.Prev

			temp.Next = node
			node.Prev = temp

			node.Next = cur
			cur.Prev = node
			break
		}else {
			count++
			cur = cur.Next
		}
	}
	this = dummyHead.Next
	return
}


func (this *MyLinkedList) DeleteAtIndex(index int)  {
	var dummyHead = &MyLinkedList{0, this, nil}
	this.Prev = dummyHead

	cur := this
	var count int
	for cur != nil {
		if count == index {
			temp := cur.Prev
			temp.Next = cur.Next
			if cur.Next != nil {
				cur.Next.Prev = temp
			}
			break
		}else {
			count++
			cur = cur.Next
		}
	}
	this = dummyHead.Next
	return
}

// tm的sb题目不给我写出来node结构体,让我错误译为给出的结构体是用来设计node,导致一步错,步步错

// 比较大的错误点在于 我多地方尝试将修改后的head重新赋值给this,但是这样是错误的,在 Go 中,方法接收器是值接收器(即使是指针接收器),对 this 的重新赋值不会改变调用者的状态,所以,本质上this并没有发生变化

206 反转列表

func reverseList(head *ListNode) *ListNode {
	// 思路,创建一个虚拟头节点,遍历链表,插入到虚拟头和下一个节点中间实现反转  头插法
	// nil-1-2-3  --> nil-2-1-3 --> nil-3-2-1
	if head == nil || head.Next == nil {
		return head
	}

	var dummyHead = &ListNode{0, head}
	// 为了方便理解,多用几个变量,本质上可以节省点
	cur := head.Next
	pre, next := head, cur.Next  // 保存头节点和 cur之后节点信息
	h := head  // 头节点
	for cur != nil {
		// insert 到底要重新指向几个元素,要看断了几条链,cur 往前往后两条链都断了,加上虚拟节点之后的链也断了共三条,需要指向三次
		dummyHead.Next = cur // 指向虚拟到cur
		cur.Next = h// 指向cur 到 头
		pre.Next = next // cur原来的pre指向原来的next

		// 重新赋值cur, pre, next, h(ead)
		h = cur // 新的头节点
		cur = next // 指向下一个节点
		pre = head // 本质上移动之后pre没有发生变动,原来的head往后移动了,可以省略
		if next != nil {
			next = next.Next // 下下节点
		}
	}
	return dummyHead.Next
}

时间n 空间1

// 简化代码
func reverseList(head *ListNode) *ListNode {
	if head == nil || head.Next == nil {
		return head
	}

	var dummyHead = &ListNode{Next: head}
	cur := head.Next
	head.Next = nil // 将原头节点的 Next 置为 nil,成为新尾节点

	for cur != nil {
		next := cur.Next
		cur.Next = dummyHead.Next
		dummyHead.Next = cur
		cur = next
	}

	return dummyHead.Next
}

func reverseList(head *ListNode) *ListNode {
	// 思路 双指针法 相当于维护一个新反转链表
	if head == nil {
		return head
	}
	var pre *ListNode // !!! 此处如果使用pre = &ListNode{} 方式进行定义,那么就初始化了!= nil
	cur := head
	for cur != nil {
		temp := cur.Next
		cur.Next = pre
		pre = cur // 新头节点
		cur = temp
	}
	return pre
}
时间n 空间1
func reverseList(head *ListNode) *ListNode {
	// 思路 通过双指针思路尝试写出递归解法
	var pre *ListNode
	return reverse(pre, head)
}

func reverse(pre, cur *ListNode) *ListNode{
	if cur == nil {
		return pre
	}
	temp := cur.Next
	cur.Next = pre
	return reverse(cur, temp)
}
时间 n 空间 n 为什么不是logn,因为本质上此递归并没有缩小问题规模,本质上还是一个一个处理节点
绷不住,递归效率最低,头插居然最高
posted @ 2024-07-19 16:54  周公瑾55  阅读(23)  评论(0)    收藏  举报