关于链表刷题

  1. 反转链表
    NC78
ListNode prev = null;
while(curr!=null){
	ListNode temp = curr.next;
	curr.next = prev;
	prev = curr;
	curr = temp;
}
// 递归反转连链表
// reverse(head.next) 返回反转之后的头结点
ListNode successor = null
if(head ==null || head.next ==null) return head
// 递归
ListNode last = reverseN(head.next)
// 链表反转
head.next.next = head
// 反转之后 原始的head头结点 变为尾节点 指向null
head.next = null

return last
  1. 反转链表的前k个节点
// 后继节点
ListNode successor = null
if(head ==null || head.next ==null) return head
if(k==1){
	// 记录第k+1个节点
	successor = head.next
	return head
}
// 递归
// 以head.next 为起点
ListNode last = reverseN(head.next,k-1)

head.next.next = head
// 反转之后的head 与后面的节点连接
head.next = successor

return last
  1. 链表在指定区间内反转[m,n]
  1. m=n时,区间只有一个节点
  2. m=1时,反转链表的前n个节点
if(m==1){
	return reverseN(head,n)}
head.next = reverseBetween(head.next,m-1,n-1)
return head
  1. 判断链表是否有环
    NC4

快慢指针

while(fast!=null || fast.next !=null){

	fast = fast.next.next;
	slow = slow.next;
	if(fast == slow){
		return true
	}
}
  1. 链表相加
    NC40

输入[9,3,7], [6,3] 输出{1,0,0,0}

解题思路:1)利用栈的先进先出,栈1存放head1元素,栈2存放head2元素
2)反转链表,再按位相加

Stack<Integer> s1 = new Stack<>()
Stack<Integer> s2 = new Stack<>()

// head1 元素依次入栈
while(head1 !=null){
	s1.push(head1.val)
	head1 = head1.next
}
//head2 元素依次入栈
while(head2 !=null){
	s2.push(head2.val)
	head2 = head2.next
}


ListNode res =null
int cnt = 0 // 进位

while(!s1.isEmpty() || !s2.isEmpty()){
	int x1 = s1.isEmpty()?0:s1.pop()
	int x2 = s2.isEmpty()?0:s2.pop()
	int sum = x1+x2+cnt  // 当前这一位的总和
	cnt = sum/10  // 查看当前是否有进位
	int val = sum%10 // 个位数
	// 进行当前节点的插入
	ListNode tempNode = new ListNode(val)
	tempNode.next = res
	res = tempNode 

}
// 最后一位有进位
if(cnt>0){
	ListNode tempNode = new ListNode(cnt);
	tempNode.next = res
	res = tempNode
}
return res
  1. 链表中环的入口节点
    NC3
// 快指针的路程是慢指针的两倍
// when fast==slow 判断有环
if(fast==slow){
	// 将快指针指向头结点  慢指针指向相遇节点
	// 同速度移动
	fast = head
	if(fast !=slow){
		fast = fast.next
		slow = slow.next
	}
	return fast
}

  1. 链表中的节点每K个一组翻转
  1. 栈 K个入栈-出栈(先进后出);判断是否为K个,已反转和未反转的拼接
Stack<ListNode> stack = new Stack<>()
ListNode res = new ListNode(0)
// p指针
ListNode  p = res
while(true){
	ListNode  temp = head
	while(temp !=null &&count <k){
		stack.push(temp)
		temp =temp.next
		count++
	}
	if(count != k){
		p.next = head
	 	break
	}
	while(!stack.isEmpty()){
		p.next(stack.pop())
		p = p.next
	}
	p.next =temp
	head =temp
}
return res.next
  1. 删除有序链表中的重复元素
    NC24
  1. 输入:1-2-3-3-4-4-5 输出1-2-5
ListNode curr = head
while(curr != null || curr.next != null){
//  继续寻找同元素的最大范围
	if(curr.val == curr.next.val){   
	   ListNode temp = curr.next
	   while(temp !=null || temp.val = curr.val){
	   		temp = temp.next
	   }
	   prev.next = temp
	   curr = temp
	}
}
  1. 输入:1-2-3-3-4-4-5 输出:1-2-3-4-5
ListNode temp = head;
while(temp.next != null){
	if(temp.val == temp.next.val){
		temp.next = temp.next.next
	}else{
		temp =temp.next
	}
}
  1. 链表的倒数第k个节点
  • 双指针

// 双指针:快慢指针 快指针先移动K步,然后快慢指针同时同速移动
for(int i=0;i<k;i++){
	fast = fast.next
}
// 表示该目标节点为头结点
if(fast == null){
	return head.next
}
if(fast.next !=null){
	fast = fast.next
	slow = slow.next
}
// 此时的slow不是倒数第k个节点,是倒数第k+1个节点
  1. 删除链表的倒数第n个节点
  • 非递归
// 求链表的长度

ListNode prev= head;
int last = length(head)-n
if(last ==0){
	return head.next}
// 求要删除节点的前节点	
for(int i=0;i<last-1;i++){
	prev= prev.next
}
prev.next =prev.next.next
return head
  • 双指针
// 同上
  1. 合并有序链表


ListNode head = new ListNode(0)
ListNode res = head;
while(l1 !=null && l2!=null){
	if(l1.val >= l2.val){
		res.next = l2
		l2 = l2.next
	}else {
		res.next = l1
		l1 = l1.next
	}
	res = res.next
}
if(l1 !=null){
		res.next = l1
	}
if(l2 != null){
		res.next = l2
	}
return head.next
  1. 合并K个有序链表
    leetcode 23
  1. 链表排序

归并排序 :先利用快慢指针找出链表的中点,然后分为两个链表,一直分,知道无法分为止,然后自底而上排序归并
时间O(NlogN) 空间O(logN)

ListNode fast =head.next
ListNode slow =head
while(fast!=null || fast.next !=null){
	fast =fast.next.next
	slow =slow.next
}
// 分割为两个链表
ListNode newList = slow.next
slow.next =null

ListNode left = sortInList(head)
ListNode right= sortInList(newList)

ListNode lhead = new ListNode(-1)
ListNode res = lhead

// 归并排序
while(left!=null &&right !=null){ 
	if(left.val > right.val){
		lhead.next = right
		right =right.next
	}else{
		lhead.next = left
		left = left.next
	}
	lhead = lhead.next
}
// 判断左右链表是否为空
lhead.next = left!=null?left:right
return res.next
  1. 判断链表是否为回文结构
  1. 寻找回文串的核心:从中心向两端扩展
  2. 单链表不能逆序遍历,将原始链表反转存入一条新的链表,然后比较
  3. 双指针(快慢指针):先找到中间节点,反转后半部分链表,然后比较节点值是否相等
// 入栈
while(temp!=null){
	stack.push(temp.val)
	temp =temp.next
}
// 出栈(后半部分即可)
len>>=1
while(len-- >0){
	if(head.val != stack.pop()){
		return false
	}
	head =head.next
	
}
  1. 两个链表的第一个公共节点

输入:{1,2,3}{4,5}{6,7} 输出{6,7}

如果有公共节点,那么两个链表指针同时走到末尾,则
若两个链表长度不等,则head1+head2 = head2+head1

ListNode p1 =head1
ListNode p2 =head2
while(p1!=p2){
	p1 = p1.next
	p2 = p2.next
	if(p1!=p2){
		if(p1 ==null){
			p1 = head2
		}
		if(p2 ==null){p2=head1}
	}
	return p1
}

双指针使用技巧

1. 快慢指针

快慢指针一般初始化为链表的头节点head,前进时快指针fast在前,慢指针slow在后
1) 链表中是否有环
2)链表中点(作用归并排序)
3)链表环的入口节点
4)寻找单链表中倒数第K个节点

2. 左右指针

左右指针一般运用在数组中,求两个索引值,初始化为left=0,right=len(arr)-1
1)二分搜索
2)反转数组
3)两数之和
4)滑动 窗口算法(字符串匹配)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值