首页 > 编程语言 >单链表相关面试算法题汇总

单链表相关面试算法题汇总

时间:2024-06-09 17:21:39浏览次数:27  
标签:head 单链 cur2 LNode nil cur1 汇总 Next 面试

技巧汇总

  • 快慢指针
  • 先找到中间节点
  • 如果要调用next..确保当前节点不为空。 依次类推。.next不为空
  • 是否有环。走过的路。重新走。互相走。
  • 画图,分解,
  • 暴力法。用hashset
  • 插入法翻转。
package main

import (
	"fmt"

	. "github.com/isdamir/gotype"
)

func AddLNode(h1, h2 *LNode) *LNode {

	// 考虑为空
	if h1 == nil || h1.Next == nil {
		return h2
	}

	if h2 == nil || h2.Next == nil {
		return h1
	}

	c := 0 // 记录进位
	sum := 0
	p1 := h1.Next
	p2 := h2.Next
	resultHead := &LNode{}

	p := resultHead

	for p1 != nil || p2 != nil {
		var p1_int int
		var p2_int int
		if p1 == nil {
			p1_int = 0
		} else {
			p1_int = p1.Data.(int)
		}
		if p2 == nil {
			p2_int = 0
		} else {
			p2_int = p2.Data.(int)
		}
		p.Next = &LNode{}
		sum = p1_int + p2_int + c

		p.Next.Data = sum % 10
		c = sum / 10
		p = p.Next
		if p1 != nil {
			p1 = p1.Next
		}
		if p2 != nil {
			p2 = p2.Next
		}

	}
	// 最后有进位
	if c == 1 {
		p.Next = &LNode{Data: 1}
	}

	return resultHead

}

// 对链表进行重新排序
// 输入 L0 -> L1 -> L2 -> Ln-1 -> Ln
// 输出 L0 -> Ln -> L2 -> Ln-1 ..
// 思路: 找到中间节点。对链表的后半部分 反转 。 前半部分和后半部分合并

//找中间节点
func findMiddleNode(head *LNode) *LNode {
	if head == nil || head.Next == nil {
		return head
	}
	// 快慢指针   1 2 3 4 5 6 7
	// fast 	1 3 5 7  fast = 7
	// slow 	1 2 3 4  slow = 4
	// slowpre  1 1 2 3  slopre = 3

	// 快慢指针   1 2 3 4 5 6 7 8
	// fast 	1 3 5 7  nil  fast = 7
	// slow 	1 2 3 4  5   slow = 5
	// slowpre  1 1 2 3  4  slopre = 4
	fast := head
	slow := head
	slowPre := head
	for fast != nil && fast.Next != nil {
		slowPre = slow
		slow = slow.Next
		fast = fast.Next.Next
	}
	slowPre.Next = nil
	return slow
}

//头节点的单链表进行翻转
func reversLNode(head *LNode) *LNode {

	// 插入法来翻转,从第二个节点开始插入
	if head == nil || head.Next == nil {
		return head
	}
	var cur *LNode
	var curnext *LNode
	cur = head.Next.Next //从第二个元素开始插入

	head.Next.Next = nil

	for cur != nil {
		// myhead 1 2 3 4
		// myhead 2 1 3 4
		curnext = cur.Next // 先获取到next 3
		// node1 :=    // 获取头节点的next 1 // 这里是xinfuzhi?
		// head.Next = cur      // 头指针 指向  2
		cur.Next = head.Next // 2 指向 1
		head.Next = cur
		// node1.Next = curnext // 1 指向3
		cur = curnext

		// curnext = cur.Next
		// cur.Next = head.Next
		// head.Next = cur
		// cur = curnext
	}
	return head
}

// 合并列表

func ReorderLNode(head *LNode) {
	if head == nil || head.Next == nil {
		return
	}

	cur1 := head
	mid := findMiddleNode(head.Next)
	head2 := &LNode{}
	head2.Next = mid
	cur2 := reversLNode(head2)
	// fmt.Println("cur1:", cur1)
	// fmt.Println("cur2:", cur2)
	PrintNode("cur1:", cur1)
	PrintNode("cur2:", cur2)

	cur1 = cur1.Next
	cur2 = cur2.Next

	for cur1.Next != nil {
		// 1 2 3 4
		// 8 7 6 5
		// 1 8  2  3 4
		// 		7  6 5
		cur1_tmp := cur1.Next // 2 3 4
		cur1.Next = cur2      // 1 8 7 6 5
		cur1 = cur1_tmp       // 2 3 4
		cur2_tmp := cur2.Next // 7 6 5
		cur2.Next = cur1      // 1 8 2 3 4
		cur2 = cur2_tmp       //      7 6 5

	}
	// 奇数  场景
	// 1 7 2 6 3
	//         5 4
	cur1.Next = cur2

}

// 找出单链表中的倒数第K个元素

// 方法一: 遍历俩遍。 第一次知道链表的个数 n . 第二次 遍历到 n-k就行。

func FindLastK1(head *LNode, k int) *LNode {

	if head == nil || head.Next == nil {
		return head
	}

	return head

}

// 方法二
// 快慢指针:  快指针fast先走k步, 从K+1开始  和 slow指针从起始位置开始继续走。 fast遇到nil停止。

func FindLastK2(head *LNode, k int) *LNode {

	if head == nil || head.Next == nil {
		return head
	}

	fast := head.Next
	slow := head.Next

	i := 0
	for i = 0; i < k && fast != nil; i++ {
		fast = fast.Next
	}

	if i < k {
		// 提前退出,不足K
		return nil
	}

	for fast != nil {
		slow = slow.Next
		fast = fast.Next
	}

	return slow

}

// 检测一个较大的单链表是否有环

//暴力法:set存储。 如果遍历到已经在set中的LNode.说明 有环。
// 快慢指针遍历法:

// 快慢指针  fast slow . 如果相遇说明有环,返回相遇点
//
func isLoop(head *LNode) *LNode {

	if head == nil || head.Next == nil {
		return head
	}

	slow := head.Next
	fast := head.Next

	for fast != nil && fast.Next != nil {
		slow = slow.Next
		fast = fast.Next.Next
		if slow == fast {
			return slow
		}
	}

	return nil
}

// 找出环的入口点。
// slow : 走了 s 步 fast走了2s步   1 2 3 4 5 6 7 -> 3
/**
a : 起点到入口点的距离
x : 入口点到遇见点的距离
L : 总长度
y : 遇见点到 入口点的步数
n : 快指针走过环的圈数 。大于等于 >= 1
r : 环的长度
如果相遇 说明
2s = s + nr   -> 2s -s = nr -> s = nr
s = a + x  // 入口点 + x
a + x = nr
a + x =  (n-1)r + r  因为 r = L - a
a + x =  (n-1)r + L - a
a = (n-1)r + L -a -x
b = L -a -x
a = (n-1) r + b
说明从相遇点开始走(n-1)r + b步一定会和初始点出发在 a相遇。

*/
//

func FindLoopNode(head *LNode, meetNode *LNode) *LNode {
	first := head.Next
	second := meetNode
	for first != second {
		first = first.Next
		second = second.Next
	}
	return first

}

// k个元素为一组进行翻转

func ReverseK(head *LNode, k int) {
	if head == nil || head.Next == nil {
		return
	}
	// k = 3
	pre := head
	begin := head.Next // 1
	var end *LNode     // 3

	var pNext *LNode // 4

	for begin != nil {
		end = begin
		for i := 1; i < k; i++ {

			if end.Next != nil {
				end = end.Next
			} else {
				break
			}
		}
		pNext = end.Next // 获取4
		end.Next = nil   //  3-> nil
		myhead := &LNode{}
		myhead.Next = begin
		pre.Next = reversLNode(myhead).Next
		begin.Next = pNext // 1 -> 4

		pre = begin   // pre = 1
		begin = pNext // begin = 4
	}

}

//合并俩个有序列表

func MergeLNode(h1 *LNode, h2 *LNode) *LNode {

	if h1 == nil || h1.Next == nil {
		return h2
	}

	if h2 == nil || h2.Next == nil {
		return h1
	}
	p1 := h1.Next
	p2 := h2.Next
	r := &LNode{}
	pre := r
	for p1 != nil && p2 != nil {
		if p1.Data.(int) < p2.Data.(int) {
			pre.Next = p1 // myhead-> 1

			pre = p1     // pre: 1
			p1 = p1.Next // p1 : 3
		} else {
			pre.Next = p2
			pre = p2
			p2 = p2.Next
		}
	}

	if p1 != nil {
		pre.Next = p1
	}

	if p2 != nil {
		pre.Next = p2
	}
	return r
}

// 给定单链表,中的某个节点的指针。 删除这个节点
// 如果是最后一个节点,没法删除

// 如果不是,则复制后面一个节点的数据。

func RemoveNode(deleteNode *LNode) bool {
	if deleteNode == nil || deleteNode.Next == nil {
		return false
	}
	// deleteNode = 5
	deleteNode.Data = deleteNode.Next.Data // 5 6 7 8 ->  6 6 7 8
	deleteNode.Next = deleteNode.Next.Next
	return true
}

// 判断俩个链表是否交叉

// 方法一:  hash .遍历添加到set中。判断。
// 方法二:  首位相接法 , l1_last -> l2_head . 判断是否有环
// 方法三:  尾节点法。 如果交叉,那么一定是同一个尾节点。
/**
你走你的。我走我的。然后我走你的,你走我的。一定会相遇
l1: 1 2 3 4 5
l2:   2 3 4 5

l1: 1 2 3 4 5 2 3 4 5
l2: 2 3 4 5 1 2 3 4 5
*/
func IsIntersect(head1 *LNode, head2 *LNode) *LNode {
	if head1 == nil || head1.Next == nil || head2 == nil || head2.Next == nil {
		return nil
	}

	cur1 := head1.Next
	cur2 := head2.Next

	for cur1 != nil && cur2 != nil {
		cur1 = cur1.Next
		cur2 = cur2.Next
		if cur1 == cur2 {
			return cur1
		}
	}

	if cur1 == nil {
		cur1 = head2.Next
		for cur1 != nil && cur2 != nil {
			cur1 = cur1.Next
			cur2 = cur2.Next
			if cur1 == cur2 {
				return cur1
			}
		}
		if cur2 == nil {
			cur2 = head1.Next
		}
		for cur1 != nil && cur2 != nil {
			cur1 = cur1.Next
			cur2 = cur2.Next
			if cur1 == cur2 {
				return cur1
			}
		}
	}
	if cur2 == nil {
		cur2 = head1.Next
		for cur1 != nil && cur2 != nil {
			cur1 = cur1.Next
			cur2 = cur2.Next
			if cur1 == cur2 {
				return cur1
			}
		}

		if cur1 == nil {
			cur1 = head2.Next
		}
		for cur1 != nil && cur2 != nil {
			cur1 = cur1.Next
			cur2 = cur2.Next
			if cur1 == cur2 {
				return cur1
			}
		}

	}
	return nil
}

// 如何 展开链接列表 除了next指针还有down指针。扁平化
/**

3 - > 11 -> 15 -> 30
6	   21    22    39
8           50    40
31                55


3 6 8 11 15 21 22 30 31 39 40 50 55

归并法: 俩俩归并 。
*/

type LDNode struct {
	data  int
	right *LDNode
	down  *LDNode
}

func (p *LDNode) Insert(headRef *LDNode, data int) *LDNode {
	newNode := &LDNode{data: data, down: headRef}
	headRef = newNode
	return headRef
}

func MergeLDNode(h1 *LDNode, h2 *LDNode) *LDNode {

	if h1 == nil {
		return h2
	}
	if h2 == nil {
		return h1
	}

	var r *LDNode
	if h1.data < h2.data {
		r = h1
		r.down = MergeLDNode(h1.down, h2)
	} else {
		r = h2
		r.down = MergeLDNode(h1, h2.down)
	}
	return r
}
func main() {
	// 计算俩个单链表所代表的数之和
	// 方式一: 遍历单链表,转换为整数。然后计算相加
	// 缺点:如果链表所代表的数很大,就无法使用

	// 方式二: 链表相加,要保留进位,长度不一样。需要继续计算
	// l1 := &LNode{}
	// l2 := &LNode{}
	// CreateNode(l1, 8)
	// CreateNode(l2, 8)
	// PrintNode("l1: ", l1)
	// PrintNode("l2: ", l2)
	// l3 := AddLNode(l1, l2)
	// PrintNode("sum: ", l3)
	// l1 := &LNode{}
	// CreateNode(l1, 8)
	// PrintNode("l1: ", l1)
	// m1 := findMiddleNode(l1.Next)
	// PrintNode("l1: ", l1)
	// fmt.Println(m1.Data)

	// l2 := &LNode{}
	// CreateNode(l2, 8)
	// PrintNode("l2:        ", l2)
	// l2r := reversLNode(l2)
	// PrintNode("l2:        ", l2r)
	// // PrintNode("翻转之后的 ", l2r)
	// l := &LNode{}
	// CreateNode(l, 8)
	// // ReorderLNode(l)
	// // PrintNode("result: ", l)
	// // r := FindLastK2(l, 3)
	// // fmt.Println(r.Data)
	// l.Next.Next.Next.Next.Next.Next = l.Next.Next.Next.Next
	// meetNode := isLoop(l)
	// if meetNode != nil {
	// 	fmt.Println("有环:", meetNode.Data)
	// 	loopNode := FindLoopNode(l, meetNode)
	// 	fmt.Print("入口Node:", loopNode.Data)
	// }
	fmt.Println("fuck ")
	// l := &LNode{}
	// CreateNode(l, 8)

	// ReverseK(l, 3)
	// PrintNode("k:", l)

	// l1 := &LNode{}
	// CreateNode(l1, 8)
	// l2 := &LNode{}
	// CreateNode(l2, 5)

	// r := MergeLNode(l1, l2)
	// PrintNode("mergeNOde: ", r)
	// fmt.Println("delete node:")
	// l := &LNode{}
	// CreateNode(l, 9)
	// PrintNode("delete before:", l)
	// RemoveNode(l.Next.Next.Next.Next.Next)
	// PrintNode("delete after:", l)

	l1 := &LNode{}
	CreateNode(l1, 9)
	l2 := &LNode{}
	CreateNode(l2, 4)
	PrintNode("L1:", l1)
	PrintNode("L2:", l2)
	l2.Next = l1.Next.Next
	l2.Next.Next.Next = l1.Next.Next.Next.Next
	PrintNode("L1:", l1)
	PrintNode("L2:", l2)

	r := IsIntersect(l1, l2)
	if r != nil {
		fmt.Println("intersect:", r.Data)
	}
}

标签:head,单链,cur2,LNode,nil,cur1,汇总,Next,面试
From: https://www.cnblogs.com/clllll/p/18239797

相关文章

  • 一些小问题汇总
    1.区分&与&&、什么是短路求值2.值<<移动的位数(十六进制下的位操作)#defineADC_SR_AWD_Pos(0U)#defineADC_SR_AWD_Msk(0x1UL<<ADC_SR_AWD_Pos)/*!<0x00000001*/#defineADC_SR_AWDADC_SR_AWD_Msk......
  • 链表--单链表
    引言:链表是一种常用的数据结构,在C语言中提供了一种灵活且高效的数据管理方式,它适用于那些需要频繁插入和删除操作的场景。链表的每个节点包含数据和指向下一个节点的指针,这种结构使得链表能够有效地利用内存,并且提供快速的插入和删除能力。然而,由于是动态分配的,访问链表中的元......
  • MyBatis-Plus 面试热点问题详解(上)
    引言MyBatis-Plus是基于MyBatis的增强工具,旨在简化MyBatis开发,提高开发效率,降低代码冗余。作为一名Java开发者,特别是在面试过程中,掌握MyBatis-Plus的相关知识是非常必要的。本文将详细介绍MyBatis-Plus在面试中的一些热点问题,帮助大家更好地准备面试。MyBatis-......
  • ​【JS重点知识04】JS执行机制(重点面试题)
     学前案例:console.log(111);setTimeout(function(){console.log(222);},1000)console.log(333);//输出结果:1111333222console.log(111);setTimeout(function(){console.log(222);},0)console.log(333);//输出结果:1113332221JS两种运行方式同......
  • 代码随想录第4天 | 24. 两两交换链表中的节点、19.删除链表的倒数第N个节点、面试题 0
    题目:24.两两交换链表中的节点思路:设置虚拟头结点,双指针+临时指针,(感觉也能递归,未尝试)时间复杂度:O(n)空间复杂度:O(1)坑:1.又忘了else{}和return2.试图访问空指针,多个条件的顺序问题及"&&""||"问题,cur->next要写在cur->next->next前面/***Definitionforsingly-linked......
  • Linux之系统故障汇总
    一、系统可能会出现的故障1、管理员密码忘记2、系统无法正常启动grub损坏(MBR损坏、grub配置文件丢失)系统初始化故障(某文件系统无法正常挂载、驱动不兼容)服务故障用户无法登录系统(bash程序故障)3、命令无法运行4、编译过程无法继续(开发环境缺少基本组件)二、单用......
  • 代码随想录算法训练营第四天 Leetcode 24 两两交换链表节点 Leetcode19 删除链表倒数
    链表问题首先要记住设置虚拟头节点Leetcode24两两交换链表节点题目链接思路:就是简单模拟两两交换 要注意链表节点的处理一定要获取到合适的位置比如:这一题中两个交换节点的前一个节点注意链表保存临时节点/***Definitionforsingly-linkedlist.*publicclas......
  • 睿抗赛-智能侦察-新手BUG汇总
    本文章主要记录本人和同学学习过程中所遇bug,报错及问题解决方法,如有雷同纯属教程看的一样。大部分问题都是没有按照流程去运行文件造成的。流程为:启动ros环境-编译-刷新环境变量-运行如果所遇问题可以通过正常流程排除掉则不需要继续阅读。1.编译报错提示“空间”不存在原......
  • 面试高频问题----6
    一、String、StringBuffer、StringBuilder1.String:***string类是java中用于表示不可变字符序列的类。***string对象是不可变的,一旦创建,其值就不能被改变。每次对string对象的修改操作都会生成一个新的string对象。***由于string的不可变性,在频繁修改字符串情况,可能会产生大......
  • 【LeetCode 随笔】面试经典 150 题【中等+困难】持续更新中。。。
    文章目录117.【中等】填充每个节点的下一个右侧节点指针II114.【中等】二叉树展开为链表129.【中等】求根节点到叶节点数字之和124.【困难】二叉树中的最大路径和173.【中等】二叉搜索树迭代器......