@TOC链表的实现及其相关习题
前言
1.链表的实现:
下文我通过递归实现
(在寻找Last元素时while循环也是可以的)
2.常见链表相关习题:【均来自LeetCode题库】
(1)反转链表。
(2)回文链表
(3)环形链表
(4)删除链表指定元素
(5)相交链表
(6)合并两个有序链表(排好序的)
二、常见链表相关习题:
1.单向链表的实现
public class MyLinkList{
//头节点
private LinkNode first=new LinkNode(null,null);
private int sive;
//添加元素
public MyLinkList(){}
// 添加元素
public void add(Object obj){
if(first.next==null){
first.next=new LinkNode(obj,null);
}else{
LinkNode lastNode=SearchlastNode(first.next);
lastNode.next=new LinkNode(obj,null);
}
sive++;
}
private LinkNode SearchlastNode(LinkNode first){
LinkNode node=first;
while(true){
if(node.next==null){
return node;
}
node=node.next;
}
}
// 在头节点增加元素
public void addHeadNode(Object obj){
if (first.next==null){
add(obj);
}else {
first.next=new LinkNode(obj,first.next.next);
}
sive++;
}
// 删除头节点
public boolean deleteHeadNode(){
if(first.next==null){
return false;
}else {
first.next=first.next.next;
sive--;
return true;
}
}
// 查询链表中元素的个数
public int size(){
return this.sive;
}
// 删除指定元素
public boolean delete(Object obj){
if(first.next==null){
return false;
}
LinkNode node=first.next;
while(node!=null){
if(obj.equals(node.date)){
return true;
}
node=node.next;
}
return false;
}
// 在指定元素后增加新元素
public boolean appointAdd(Object obj,Object newobj){
if(first.next==null){
return false;
}
LinkNode node=first.next;
while(node!=null){
if(obj.equals(node.date)){
if(node.next==null){
node.next=new LinkNode(newobj,null);
return true;
}
node.next=new LinkNode(newobj,node.next.next);
return true;
}
node=node.next;
}
return false;
}
}
class LinkNode {
public Object date;
public LinkNode next;
public LinkNode(Object obj, LinkNode next){
// 是梦开始的起点也是 所有过程的必经之路!!
this.next=next;
this.date=obj;
}
@Override
public String toString() {
return (String) date;
}
}
2.链表相关习题
(1).反转链表:(LeetCode题目编号:206)
这里我采用三指针移动的方法解决(当然也可以暴力递归或者循环)
如图:
代码如下:
* public class ListNode {
* int val;
* ListNode next;
* ListNode(int x) { val = x; }
* }
class Solution {
public ListNode reverseList(ListNode head) {
return help(head);
}
public ListNode help(ListNode head){
if(head==null || head.next==null)return head;
if(head.next.next==null){
ListNode jj=head;
ListNode ii=jj.next;
ii.next=jj;
jj.next=null;
return ii;
}
ListNode i,j,t;
i=head;
j=head.next;
t=head.next.next;
i.next=null;
while(true){
j.next=i;
i=j;
if(t==null)break;
j=t;
t=t.next;
}
return j;
}
}
还可以暴力递归 你们随便看看就行(虽然也可以但是空间,时间复杂度都比较大)
简单来说就是先遍历存储到栈中再递归弹栈形成反转链表newhead
class Solution {
ListNode newhead;
Stack<ListNode> stack;
public ListNode reverseList(ListNode head) {
if(head==null)return null;
Stack<ListNode> stack=new Stack<>();
while(head!=null){
stack.push(head);
head=head.next;
}
this.stack=stack;
this.newhead=this.stack.pop();
return digui(newhead);
}
public ListNode digui(ListNode head){
if(!this.stack.isEmpty()){
head.next=this.stack.pop();
return digui(head.next);
}else{
head.next=null;
return this.newhead;
}
}
}
(2)回文链表:(LeetCode题目编号:234)
1.先展示一个效率比较低但是很好理解的方法:(效率高的版本博主尚未理解和哈哈哈哈实现嘻嘻!)
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode(int x) { val = x; }
* }
*/
class Solution {
public boolean isPalindrome(ListNode head) {
//new一个双端队列
ArrayDeque<ListNode> deque=new ArrayDeque<>();
//将链表中的元素循环存入双端队列中
while(head!=null){
deque.addFirst(head);
head=head.next;
}
//再 循环出列判断首尾是否一致 若不一致则直接返回false
while(!deque.isEmpty()){
/*
这里我们需要考虑到最后firs出列和last出列为同一个对象的情况
相当于正好走到了队列的正中央
removeFirst()方法会检索队列首元素并删除
getFirst()方法会检索队列首元素但不删除
removeLast()与getLast()同理
*/
if(deque.getFirst()==deque.getLast())return true;
if(deque.removeFirst().val != deque.removeLast().val) return false;
}
return true;
}
}
(3)环形链表:(LeetCode题目编号:141)
这一题配图会好理解一些:
这一题咋们不得不说快慢指针了:(以下是我形象的解释哈哈哈)
假设乌龟和兔子起点都在头节点
快慢指针:
乌龟跑的慢 所以 乌龟是后进入环的
兔子跑的快 所以 兔子是先进入环的
当两者都进入环
**兔子一定会追乌龟(因为兔子跑的快嘛!!)
当乌龟与兔子相遇则
存在环
若兔子走到了null(如果没环那大家都没进入环 最终都走到了最后一个节点 next==null;)
则无环
代码如下:(实力有限,我写的并不太简洁)
/**
* Definition for singly-linked list.
* class ListNode {
* int val;
* ListNode next;
* ListNode(int x) {
* val = x;
* next = null;
* }
* }
*/
public class Solution {
LinkedList list=new LinkedList();
public boolean hasCycle(ListNode head) {
ListNode l1=head;
if(l1==null){
return false;
}
ListNode l2= head.next;
if (l2==null ||l2.next==null || l2.next.next==null){
return false;
}
while(true){
if (l2.next==null || l2.next.next==null){
return false;
}
if(l1.equals(l2)){
return true;
}
l1=l1.next;
l2=l2.next.next;
}
}
}
(4)删除链表指定元素:(LeetCode题目编号:203)
我是暴力递归可能有点难理解 需要你细心阅读
当然也可以循环实现 (博主脑子中毒了)
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode(int x) { val = x; }
* }
*/
class Solution {
int val;
ListNode head;
public ListNode removeElements(ListNode head, int val) {
this.head=head;
this.val=val;
// 一个都没有
if(head==null){
return head;
}
// 正好只有一个
if(head.next==null){
if(head.val==val){
head=null;
return head;
}
return head;
}
// 两个及两个以上
return removeElementsGo(head);
}
public ListNode removeElementsGo(ListNode head){
if(head==null){
return this.head;
}
if(this.head.val==this.val){
this.head=head.next;
head=null;
return removeElementsGo(this.head);
}
if(head.next==null && head.val==this.val){
head=null;
return this.head;
}else if(head.next==null){
return this.head;
}
if(head.next.val==this.val){
head.next=head.next.next;
return removeElementsGo(head);
}else{
return removeElementsGo(head.next);
}
}
}
(5)相交链表:(LeetCode题目编号:160)
这一题也上图吧:
我也是暴力解法嘻嘻:
意思就是 先算每个链表的长度n1,n2 再让较长的链表先走|n1-n2|步
然后同起点进行逐个判断是否相交 如果不相交 最终都走到尾节点 next=null;
代码如下:
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode(int x) {
* val = x;
* next = null;
* }
* }
*/
public class Solution {
public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
if(headA==null || headB==null)return null;
int a=length(headA);
int b=length(headB);
if(a>b){
return getIntersectionNodeGo(headA,headB,a,b);
}else if(a<b){
return getIntersectionNodeGo(headB,headA,b,a);
}else{
ListNode i=headA;
ListNode j=headB;
while(true){
if(i==null || j==null){
return null;
}
if(i.equals(j)){
return i;
}
i=i.next;
j=j.next;
}
}
}
public int length(ListNode head){
int length=0;
while(head!=null){
length++;
head=head.next;
}
return length;
}
public ListNode getIntersectionNodeGo(ListNode headA, ListNode headB,int a,int b){
ListNode i=headA;
ListNode j=headB;
while(true){
i=i.next;
a--;
if(a==b)break;
}
while(true){
if(i==null || j==null){
return null;
}
if(i.equals(j)){
return i;
}
i=i.next;
j=j.next;
}
}
}
(6)合并两个有序链表:(LeetCode题目编号:21)
这一题递归实现 代码十分简洁
可以这么理解我的解法:
先是递归描点(排好序的点【节点】) 然后return是连线的过程
代码如下:
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode() {
}
* ListNode(int val) {
this.val = val;
}
* ListNode(int val, ListNode next) {
this.val = val; this.next = next;
}
* }
*/
class Solution {
public ListNode mergeTwoLists(ListNode l1, ListNode l2){
if(l1 == null) { return l2; }
if(l2 == null) { return l1; }
ListNode ll=l1.val<l2.val? l1:l2;
if(l1.val>=l2.val){
ll.next=mergeTwoLists(ll.next,l1);
}else{
ll.next=mergeTwoLists(ll.next,l2);
}
return ll;
}
}
总结
你需要掌握
1.熟练的实现链表 信手拈来
2.掌握常见题型的 基本解法 一定要有自己的想法 和理解 即使你看不懂别人写的
3.我为实现的高效率版本 回文链表判断 你可以写完教我 私信我嘻嘻!!
4.谢谢你的观看噢!(学累了吧来看点治愈人心的图片)
别冲了!保重!