一.自己实现一个简单的单链表
public class MySingleList {
static class Node{
public int val;//代表节点的数据
public Node next;//存储下一个节点的地址
public Node(int val) {
this.val = val;
}
}
public Node head;//代表当前链表的头节点
//创建链表
public void createLink(){
Node node1=new Node(12);
Node node2=new Node(12);
Node node3=new Node(12);
Node node4=new Node(12);
Node node5=new Node(12);
node1.next=node2;
node2.next=node3;
node3.next=node4;
node4.next=node5;
head=node1;
}
//遍历链表
public void display(){
Node cur=head;
while(cur!=null){
System.out.print(cur.val+" ");
cur=cur.next;
}
}
//查找单链表中是否包含关键字key
public boolean contains(int key){
Node cur=head;
while(cur!=null){
//如果是引用类型要重写equals
if(cur.val==key){
return true;
}
cur=cur.next;
}
return false;
}
//得到链表的长度
public int size(){
int count=0;
Node cur=head;
while(cur!=null){
count++;
cur=cur.next;
}
return count;
}
//头插法,O(1)
public void addFirst(int val){
Node node=new Node(val);
node.next=head;
head=node;
}
//尾插法,O(n)
public void addLast(int val){
Node node=new Node(val);
Node cur=head;
//注意判断head==null的情况,否则会空指针异常
if(cur==null){
head=node;
return;
}
while(cur.next!=null){
cur=cur.next;
}
cur.next=node;
}
//任意位置插入节点
public void addIndex(int index,int val){
checkIndex(index);
if(index==0){
addFirst(val);
return;
}
if(index==size()){
addLast(val);
return;
}
Node node=new Node(val);
Node cur=findIndexSubOne(index);
node.next=cur.next;
cur.next=node;
}
//找出插入位置的前一个节点
private Node findIndexSubOne(int index){
Node cur=head;
while(index-1!=0){
cur=cur.next;
}
return cur;
}
//检查index位置合法性
private void checkIndex(int index) {
if(index<0||index>size()){
throw new ListIndexOutOfException("index位置不合法");
}
}
//查找key节点的前一个节点
private Node searchPrev(int key){
if(head==null){
return null;
}
Node cur=head;
while(cur.next!=null){
if(cur.next.val==key){
return cur;
}
cur=cur.next;
}
return null;//没有需要删除的节点
}
//删除第一个出现的关键字为key的节点
public void remove(int key){
if(head==null){
return;
}
if(head.val==key){
head=head.next;
return;
}
Node cur=searchPrev(key);
if(cur==null){
return;
}
cur.next=cur.next.next;
}
//删除所有值为key的节点
public void removeAll(int key){
if(head==null){
return;
}
while (head!=null&&head.val==key){
head=head.next;
}
Node cur=head;
while(cur!=null&&cur.next!=null){
if(cur.next.val==key){
cur.next=cur.next.next;
}else{
cur=cur.next;
}
}
}
//清空链表
public void clear(){
head=null;
}
}
二.相关题目练习
1.删除链表中所有指定值的节点(https://leetcode.cn/problems/remove-linked-list-elements/)。
实例:
思路:
实现:
public ListNode removeElements(ListNode head, int val) {
if(head==null){
return null;
}
ListNode cur=head.next;
ListNode prev=head;
while(cur!=null){
if(cur.val==val){
prev.next=cur.next;
cur=cur.next;
}else{
prev=cur;
cur=cur.next;
}
}
if(head.val==val){
head=head.next;
}
return head;
}
2.反转一个单链表(https://leetcode.cn/problems/reverse-linked-list/)
例子:
思路:
实现:
public ListNode reverseList(ListNode head) {
if(head==null){
return null;
}
if(head.next==null){
return head;
}
ListNode cur=head.next;
head.next=null;
while(cur!=null){
ListNode curNext=cur.next;
cur.next=head;
head=cur;
cur=curNext;
}
return head;
}
3.返回链表的中间节点(https://leetcode.cn/problems/middle-of-the-linked-list/)
例子:
思路:
实现:
public ListNode middleNode(ListNode head) {
ListNode slow=head;
ListNode fast=head;
while(fast!=null&&fast.next!=null){
slow=slow.next;
fast=fast.next.next;
}
return slow;
}
4.返回倒数第k个节点(https://www.nowcoder.com/practice/886370fe658f41b498d40fb34ae76ff9?tpId=196&tqId=37100&rp=1&ru=/exam/oj&qru=/exam/oj&sourceUrl=%2Fexam%2Foj%3Fpage%3D1%26pageSize%3D50%26search%3D%25E9%2593%25BE%25E8%25A1%25A8%25E4%25B8%25AD%25E5%2580%2592%25E6%2595%25B0%26tab%3D%25E7%25AE%2597%25E6%25B3%2595%25E7%25AF%2587%26topicId%3D196&difficulty=undefined&judgeStatus=undefined&tags=&title=%E9%93%BE%E8%A1%A8%E4%B8%AD%E5%80%92%E6%95%B0)
例子:
思路:
实现:
public ListNode FindKthToTail (ListNode pHead, int k) {
if(k<=0||pHead==null){
return null;
}
ListNode fast=pHead;
ListNode slow=pHead;
//fast走k-1步
while(k-1!=0){
fast=fast.next;
//fast==null时说明k的值要大于链表的长度
if(fast==null){
return null;
}
k--;
}
while(fast.next!=null){
fast=fast.next;
slow=slow.next;
}
return slow;
}
5.合并两个有序链表(https://leetcode.cn/problems/merge-two-sorted-lists/)
例子:
思路:
实现:
public ListNode mergeTwoLists(ListNode list1, ListNode list2) {
if(list1==null&&list2==null){
return null;
}
ListNode newHead=new ListNode();
ListNode temp=newHead;
while(list1!=null&&list2!=null){
if(list1.val<list2.val){
temp.next=list1;
list1=list1.next;
temp=temp.next;
}else{
temp.next=list2;
list2=list2.next;
temp=temp.next;
}
}
if(list1==null){
temp.next=list2;
}
if(list2==null){
temp.next=list1;
}
return newHead.next;
}
6.以一个基准值分割一个链表(https://www.nowcoder.com/questionTerminal/0e27e0b064de4eacac178676ef9c9d70)
思路:
实现:
public ListNode partition(ListNode pHead, int x) {
ListNode bs=null;
ListNode be=null;
ListNode as=null;
ListNode ae=null;
ListNode cur=pHead;
while(cur!=null){
if(cur.val<x){
if(bs==null){
bs=cur;
be=cur;
}else{
be.next=cur;
be=be.next;
}
}else{
if(as==null){
as=cur;
ae=cur;
}else{
ae.next=cur;
ae=ae.next;
}
}
cur=cur.next;
}
//第一段为空,返回第二段
if(bs==null){
return as;
}
//第二段为空,返回第一段
if(as==null){
return bs;
}
be.next=as;
ae.next=null;
return bs;
}
7.链表的回文结构(https://leetcode.cn/problems/palindrome-linked-list/submissions/)
例子:
思路:
实现:
public boolean isPalindrome(ListNode head) {
if(head==null){
return true;
}
if(head.next==null){
return true;
}
ListNode slow=head;
ListNode fast=head;
while(fast!=null&&fast.next!=null){
slow=slow.next;
fast=fast.next.next;
}
ListNode cur=slow.next;
ListNode prev=slow;
while(cur!=null){
ListNode curNext=cur.next;
cur.next=prev;
prev=cur;
cur=curNext;
}
while(head!=prev){
if(head.val!=prev.val){
return false;
}
//偶数的情况
if(head.next==slow){
return true;
}
head=head.next;
prev=prev.next;
}
return true;
}
8.找出链表的公共节点(https://leetcode.cn/problems/intersection-of-two-linked-lists/)
思路:
实现:
public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
int lengthA=0;
int lengthB=0;
ListNode pA=headA;
ListNode pB=headB;
while(pA!=null){
lengthA++;
pA=pA.next;
}
while(pB!=null){
lengthB++;
pB=pB.next;
}
//确保pA是长链表,len>0
int len=lengthA-lengthB;
pA=headA;
pB=headB;
if(len<0){
pA=headB;
pB=headA;
len=-len;
}
while(len!=0){
pA=pA.next;
len--;
}
while(pA!=pB){
pA=pA.next;
pB=pB.next;
}
return pA;
}
9.判断链表是否有环(https://leetcode.cn/problems/linked-list-cycle/submissions/)
思路:快慢指针,slow每次走一步,fast每次走二步,slow==fasst说明有环,否则没环
实现:
public boolean hasCycle(ListNode head) {
if(head==null){
return false;
}
ListNode slow=head;
ListNode fast=head;
while(fast!=null&&fast.next!=null){
slow=slow.next;
fast=fast.next.next;
if(slow==fast){
return true;
}
}
return false;
}
10.返回链表入环的第一个节点(https://leetcode.cn/problems/linked-list-cycle-ii/)
思路:相遇点与入口点的距离和起始点与入口点的距离相等
实现:
public ListNode detectCycle(ListNode head) {
if(head==null){
return null;
}
ListNode slow=head;
ListNode fast=head;
while(fast!=null&&fast.next!=null){
slow=slow.next;
fast=fast.next.next;
if(slow==fast){
break;
}
}
if(fast==null||fast.next==null){
return null;
}
while(fast!=head){
fast=fast.next;
head=head.next;
}
return fast;
}
三.模拟一个简单的LinkedList
public class MyLinkedList {
static class ListNode{
public int val;
public ListNode prev;
public ListNode next;
public ListNode(int val) {
this.val = val;
}
}
public ListNode head;
public ListNode last ;
//头插,O(1)
public void firstAdd(int val){
ListNode node=new ListNode(val);
if(head==null){
head=node;
last = node;
return;
}
node.next=head;
head.prev=node;
head=node;
}
//尾插,O(1)
public void lastAdd(int val){
ListNode node=new ListNode(val);
if(head==null){
head=node;
last=node;
return;
}
last.next=node;
node.prev=last;
last=node;
}
//任意位置插入
public void addIndex(int index,int val){
if(index<0||index>size()){
return;
}
if(index==0){
firstAdd(val);
return;
}
if (index==size()){
lastAdd(val);
return;
}
ListNode node =new ListNode(val);
ListNode cur=head;
while(index!=0){
cur=cur.next;
index--;
}
node.next=cur;
node.prev=cur.prev;
cur.prev.next=node;
cur.prev=node;
}
//删除链表中值为key的节点
public void remove(int key){
ListNode cur=head;
while(cur!=null){
if(cur.val==key){
if(cur==head){
head=head.next;
if(head!=null){
head.prev=null;
}
return;
}
if(cur==last){
last=last.prev;
last.next=null;
return;
}
cur.prev.next=cur.next;
cur.next.prev=cur.prev;
return;
}else{
cur=cur.next;
}
}
}
//删除链表中所有值为key的节点
public void removeAll(int key){
ListNode cur=head;
while(cur!=null){
if(cur.val==key){
if(cur==head){
head=head.next;
if(head!=null){
head.prev=null;
}
}else{
cur.prev.next=cur.next;
if(cur.next!=null){
cur.next.prev=cur.prev;
}else{
last=last.prev;
}
}
}
cur=cur.next;
}
}
//打印双向链表
public void display(){
ListNode cur=head;
while(cur!=null){
System.out.print(cur.val+" ");
cur=cur.next;
}
}
//链表长度
public int size(){
int len=0;
ListNode cur=head;
while(cur!=null){
len++;
cur=cur.next;
}
return len;
}
//链表是否包含key
public boolean contains(int key){
ListNode cur=head;
while(cur!=null){
if(cur.val==key){
return true;
}
cur=cur.next;
}
return false;
}
//链表置空
public void clear(){
ListNode cur=head;
while (cur!=null){
ListNode curNext=cur.next;
cur.prev=null;
cur.next=null;
cur=curNext;
}
head=null;
last=null;
}
}
不同点 | ArrayList | LinkedList |
---|---|---|
存储空间上 | 物理上一定连续 | 逻辑上连续,但物理上不一定连续 |
随机访问 | 支持O(1) | 不支持O(n) |
头插 | 需要移动元素,O(n) | 只需要修改引用的指向,O(1) |
插入 | 空间不够需要扩容 | 没有容量的概念 |
引用场景 | 元素高效存储+频繁访问 | 任意位置插入和删除元素 |