文章目录
- 前言
- 一、单链表
- 二、实现单链表及部分单链表练习
- 1.先创建一个节点
- 2.头插法 -addFirst方法的实现
- 3.尾插法 -addLast方法的实现
- 4.任意位置插入,第一个数据为0号下标,-addIndex方法的实现
- 5.获取链表长度-getLength方法的实现
- 6.判断单链表中是否有某个元素-contains方法的实现
- 7.删除第一次data/value为key的节点-remove方法的实现
- 8.删除所有data/value为key的节点-removeAllKey方法的实现
- 9.单链表show方法的实现
- 10.翻转一个单链表(头插法)--reverseList方法的实现
- 11.找到单链表的中间节点--middleNode方法的实现
- 12.找到链表开始入环的第一个节点--ringEntrance方法的实现
- 13.给定一个链表,判断链表中是否有环--hasCycle方法的实现
- 13. 链表的回文结构 --chkPalindrome方法的实现
- 14.删除链表重复节点--deleteDuplication方法的实现
- 15. 链表分割,以给定值x为基准将链表分割成两部分,所有小于x的结点排在大于或等于x的结点之前--partition方法的实现
- 16.输入一个链表,输出该链表中倒数第k个结点--FindKthToTail方法的实现
- 总结
前言
链表是一种物理存储单元上非连续、非顺序,但在逻辑上是有顺序的存储结构,数据元素的逻辑顺序是通过链表中的指针链接次序实现的
一、单链表
链表中的数据是以节点来表示的,每个节点的构成:元素(数据元素的映象) + 指针(指示后继元素存储位置),元素就是存储数据的存储单元,指针就是连接每个节点的地址数据。
以“节点的序列”表示线性表称作线性链表(单链表),单链表是链式存取的结构。
存放节点值的数据域 | 存放节点的下一个节点的地址的链域 |
data | next
data | next
以两个节点为例,假设第二个节点得地址是0x111,那么第一个节点的next存储得就是0x111,这样一次链接,就可以形成一个单链表,第一个节点是单链表得头节点用head标识,且最后一个节点得next为null.
二、实现单链表及部分单链表练习
1.先创建一个节点
class Node {
public int val;
public Node next;
public Node(int val) {
this.val = val;
}
}
2.头插法 -addFirst方法的实现
public class MyLinkedList { //创建单链表
public Node head = null;
//头插法
public void addFirst(int val) {
Node node = new Node(val);
node.next = this.head;
this.head = node;
}
3.尾插法 -addLast方法的实现
public void addLast(int val) {
Node node = new Node(val);
if (this.head == null) {
this.head = node;
} else {
Node cur = this.head;
while (cur.next != null) {
cur = cur.next;
}
cur.next = node;
}
}
4.任意位置插入,第一个数据为0号下标,-addIndex方法的实现
//任意位置插入,第一个数据为0号下标
public void addIndex(int index, int data) {
//位置的合法性
if (index < 0 || index > getlength()) {
System.out.println("插入的位置不合法!");
return;
}
if (index == 0) {
addFirst(data); //头插法
return;
}
if (index == getlength()) {
addLast(data); //尾插法
return;
}
//中间插入
Node ret = searchIndexSubOne(index);
//ret 存储的是index-1位置的节点的
Node node = new Node(data);
node.next = ret.next;
ret.next = node;
}
其中searchIndexSubOne(index)方法为找到要插入节点的前一个节点,具体的实现:
public Node searchIndexSubOne(int index) {
Node cur = this.head;
while (index - 1 != 0) {
cur = cur.next;
index--;
}
return cur;
}
5.获取链表长度-getLength方法的实现
public int getlength() {
int len = 0;
Node cur = this.head;
while (cur != null) {
len++;
cur = cur.next;
}
return len;
}
6.判断单链表中是否有某个元素-contains方法的实现
public boolean contains(int key) {
Node cur = this.head;
while (cur != null) {
if (cur.val == key) {
return true;
}
cur = cur.next;
}
return false;
}
7.删除第一次data/value为key的节点-remove方法的实现
public void remove(int key) {
if (this.head.val == key) {
this.head = this.head.next;
return;
}
//先找到前驱节点
Node pre = searchPrev(key);
if (pre == null) {
System.out.println("没有找到要删除的元素");
} else {
Node del = pre.next;
pre.next = del.next;
}
}
其中 searchPrev(key)方法是为了找到前驱节点,即要删除的前一个节点,具体的实现:
public Node searchPrev(int key) {
Node cur = this.head;
while (cur != null) {
while (cur.next.val == key) {
return cur;
}
cur = cur.next;
}
return null;
}
8.删除所有data/value为key的节点-removeAllKey方法的实现
public void removeAllKey(int key) {
Node cur = this.head.next;
Node pre = this.head;
while (cur != null) {
if (cur.val == key) {
pre.next = cur.next;
cur = cur.next;
} else {
pre = cur;
cur = cur.next;
}
}
if (this.head.val == key) {
this.head = this.head.next;
}
}
9.单链表show方法的实现
public void show() {
Node cur = this.head;
while (cur != null) {
System.out.print(cur.val + " ");
cur = cur.next;
}
}
10.翻转一个单链表(头插法)–reverseList方法的实现
public Node reverseList() {
if (this.head == null && this.head.next == null) {
return null;
}
Node cur = this.head;
Node curNext = this.head.next;
cur.next = null;// 如果没有将第一个next清除就会变成循环
cur = curNext;
while (cur != null) {
curNext = curNext.next;
cur.next = this.head;
this.head = cur;
cur = curNext;
}
return this.head;
}
11.找到单链表的中间节点–middleNode方法的实现
//快慢指针法 来找中间节点
public int middleNode() {
if (this.head == null) {
return this.head.val;
}
Node fast = this.head;
Node slow = this.head;
while (fast != null && fast.next != null) { //用&&来区分奇偶情况,满足其中一个情况就结束;
// 偶节点fast=null ;奇节点fast.next=null停止
fast = fast.next.next;
slow = slow.next;
}
return slow.val;
}
12.找到链表开始入环的第一个节点–ringEntrance方法的实现
public Node ringEntrance() {
Node fast = this.head;
Node slow = this.head;
while (fast != null && fast.next != null) {
fast = fast.next.next;
slow = slow.next;
if (fast == slow) {
break;
}
}
if (fast == null || fast.next == null) {
return null;
}
slow = this.head;
while (slow != fast) {
slow = slow.next;
fast = fast.next;
}
return slow;
}
13.给定一个链表,判断链表中是否有环–hasCycle方法的实现
public boolean hasCycle() {
Node fast = this.head;
Node slow = this.head;
while (fast != null && fast.next != null) {
fast = fast.next.next;
slow = slow.next;
if (fast == slow) {
return true;
}
}
if (fast == null || fast.next == null) {
return false;
}
return true;
}
13. 链表的回文结构 --chkPalindrome方法的实现
public boolean chkPalindrome() {
if (this.head == null) {
return false;
}
if (this.head.next == null) {
return true;
}
Node fast = this.head;
Node slow = this.head;
while (fast != null && fast.next != null) {
fast = fast.next.next;
slow = slow.next;
}
//翻转从中间节点起后面的节点 三个变量
Node cur = slow.next;
while (cur != null) {
Node curNext = slow.next;
cur.next = slow;
slow = cur;
cur = curNext;
}
while (this.head != slow) {
//奇数情况
if (this.head.val != slow.val) {
return false;
}
//偶数情况
if (this.head.next == slow) {
return true;
}
this.head = this.head.next;
slow = slow.next;
}
return true;
}
14.删除链表重复节点–deleteDuplication方法的实现
public Node deleteDuplication() {
Node cur = this.head;
Node tmpHead = new Node(-1);
Node newHead = tmpHead;
while (cur != null) {
if (cur.next != null && cur.val == cur.next.val) {
while (cur.next != null && cur.val == cur.next.val) {
cur = cur.next;
}
cur = cur.next;
} else {
tmpHead.next = cur;
tmpHead = tmpHead.next;
cur = cur.next;
}
}
tmpHead.next = null;
return newHead.next;
}
15. 链表分割,以给定值x为基准将链表分割成两部分,所有小于x的结点排在大于或等于x的结点之前–partition方法的实现
public Node partition(int x) {
Node cur = this.head;
Node bs = null; //将节点分为两部分大于x 和小于等于x
Node be = null; // bs表示大于x节点的头部(开始) be表示大于x节点的尾部
Node as = null; //as表示小于等于x节点的头部(开始) ae表示小于等于x节点的尾部
Node ae = null;
while (cur != null) {
if (cur.val < x) { //大于x
if (bs == null) {
bs = cur;
be = bs;
} else {
be.next = cur;
be = be.next;
}
//小于等于x
} else {
if (as == null) {
as = cur;
ae = as;
} else {
ae.next = cur;
ae = ae.next;
}
}
cur = cur.next;
}
if (bs != null) {
this.head = bs;
be.next = as;
} else {
this.head = as;
}
if (ae != null) {
ae.next = null;
}
return this.head;
}
16.输入一个链表,输出该链表中倒数第k个结点–FindKthToTail方法的实现
思路:快慢指针 快指针先走k-1步 ;慢指针再走,就会相差k-1步,相差k个节点,当快指针走到最后一个节点是,慢指针刚好到k;
public Node FindKthToTail(int k) {
if (head == null) {
return null;
}
if (k <= 0) {
return null;
}
Node fast = this.head;
Node slow = this.head;
while (k - 1 != 0) {
if (fast.next != null) { //这注意!!防止空指针
fast = fast.next;
k--;
} else {
System.out.println("给的k值太大");
return null;
}
while (fast.next != null) {
fast = fast.next;
slow = slow.next;
}
}
return slow;
}
}
总结
链表的问题需要逻辑严谨,不断地修改调试,代码太多debug找错点也很困难,需要反复实践,找到最快的断点来找出bug