移除元素
移除头结点和移除其他节点的操作是不一样的,因为链表的其他节点都是通过前一个节点来移除当前节点,而头结点没有前一个节点。
所以头结点如何移除呢,其实只要将头结点向后移动一位就可以,这样就从链表中移除了一个头结点。
但那需要单独写一段逻辑来处理移除头结点的情况,其实可以设置一个虚拟头结点,这样原链表的所有节点就都可以按照统一的方式进行移除了。
package LianBiao;
/**
* 功能:
* 作者:RNG·Cryin
* 日期:2024-06-24 9:08
*/
public class YiChuYuanSu {
public ListNode solution(ListNode head, int val){
if(head == null) return head;
ListNode dummyHead = new ListNode(-1,head);
ListNode cur = new ListNode();
ListNode pre = new ListNode();
pre = dummyHead;
cur = head;
int flag = 0 ;
while (cur != null ){
if(cur.val == val){
pre.next = cur.next;
flag = flag +1;
System.out.println("已移除");
}else {
pre = cur;
}
cur = cur.next;
}
if (flag == 0) System.out.println("未找到该值");
return head;
}
public static void main(String[] args) {
ListNode head = new ListNode(1);
// 定义一个临时节点变量,用于构建链表
ListNode current = head;
// 使用循环添加剩余的节点
for (int i = 2; i <= 10; i++) {
current.next = new ListNode(i);
current = current.next; // 移动到刚添加的节点,以便继续添加下一个节点
}
YiChuYuanSu yiChuYuanSu = new YiChuYuanSu();
ListNode resultHead = yiChuYuanSu.solution(head,9);
// 打印链表的所有元素
current = head; // 将current重置为头节点,准备遍历
while (current != null) {
System.out.print(current.val+",");
current = current.next;
}
}
}
ListNode如下
package LianBiao;
/**
* 功能:
* 作者:RNG·Cryin
* 日期:2024-06-24 9:41
*/
public class ListNode {
// 结点的值
int val;
// 下一个结点
ListNode next;
// 节点的构造函数(无参)
public ListNode() {
}
// 节点的构造函数(有一个参数)
public ListNode(int val) {
this.val = val;
}
// 节点的构造函数(有两个参数)
public ListNode(int val, ListNode next) {
this.val = val;
this.next = next;
}
}
设计链表
就是实现链表的增删改查。
get(index):获取链表中第 index 个节点的值。如果索引无效,则返回-1。
addAtHead(val):在链表的第一个元素之前添加一个值为 val 的节点。插入后,新节点将成为链表的第一个节点。
addAtTail(val):将值为 val 的节点追加到链表的最后一个元素。
addAtIndex(index,val):在链表中的第 index 个节点之前添加值为 val 的节点。如果 index 等于链表的长度,则该节点将附加到链表的末尾。如果 index 大于链表长度,则不会插入节点。如果index小于0,则在头部插入节点。
deleteAtIndex(index):如果索引 index 有效,则删除链表中的第 index 个节点。
关键还是在于虚拟头结点,这样的话插入头部,也和普通点一样处理。
package LianBiao;
/**
* 功能:
* 作者:RNG·Cryin
* 日期:2024-07-01 20:31
*/
public class LinkedList {
int size;
ListNode dhead ;
ListNode head;
public LinkedList(ListNode head, int size) {
this.size = size ;
dhead = new ListNode(-1);
dhead.next = head;
this.head = head ;
}
public int get(int index){
if (index < 0 || index >= size){
return -1;
}
ListNode cur = dhead ;
for (int i = 0 ; i <= index; i++){
cur = cur.next;
}
return cur.val;
}
public void addAtIndex(int index, int val){
if (index < 0){
index = 0;
};
if (index > size){
index = size ;
}
ListNode pre = dhead;
ListNode cur = head;
for (int i = 0;i < index;i++){
pre = cur;
cur = cur.next;
} //for循环结束后,cur就是索引为index的节点。
ListNode newNode = new ListNode(val);
newNode.next = cur;
pre.next = newNode ;
size++;
}
public void deleteAtIndex(int index){
if (index < 0 || index >= size){
return ;
}
ListNode pre = dhead;
ListNode cur = head ;
for (int i = 0; i < index ; i++ ){
pre = cur;
cur = cur.next;
} //执行完,cur一定是索引为Index的元素。
pre.next = cur.next; //因为是单链表,就只修改Next就够了
size--;
}
public static void main(String[] args) {
//定义一个size=10 ,1 2 3 4 .。。的链表
ListNode head = new ListNode(1);
// 定义一个临时节点变量,用于构建链表
ListNode current = head;
// 使用循环添加剩余的节点
for (int i = 2; i <= 10; i++) {
current.next = new ListNode(i);
current = current.next; // 移动到刚添加的节点,以便继续添加下一个节点
}
LinkedList operateLink = new LinkedList(head,10);
operateLink.addAtIndex(0,11); //在头部插入
ListNode dhead = operateLink.dhead ;
ListNode cur = dhead.next;
for (int i = 0 ; i< operateLink.size ; i++){
System.out.print(cur.val+",");
cur = cur.next;
}
}
}
翻转链表
有普通翻转和递归两种方法。普通方法是定位到pre cur temp,就可以修改cur指针翻转了,然后三个指针前进一步。递归方法是,传参一定需要传进去pre,因为单链表靠cur是拿不到前一个点信息的。
package LianBiao;
/**
* 功能:
* 作者:RNG·Cryin
* 日期:2024-07-03 10:43
*/
public class FanzhuanLianBiao {
public ListNode reverse(ListNode head){
//判断非法
if (head == null) return null;
//三个位置
ListNode pre = null;
ListNode cur = head;
ListNode temp = null;
while (cur!=null){
temp = cur.next;
cur.next = pre ;
pre = cur;
cur = temp;
}
return pre;
}
public ListNode DiGuiReverseList(ListNode head){
return DiGuiReverse(null,head);
}
public ListNode DiGuiReverse(ListNode pre , ListNode cur){
//先判断非法
if (cur == null) return pre;
ListNode temp = cur.next;
cur.next = pre ;
return DiGuiReverse(cur,temp);
}
public static void main(String[] args) {
ListNode head = new ListNode(1,null);
ListNode cur = head;
for (int i=2; i<=10;i++){
// ListNode listNode = new ListNode(i);
// cur.next = listNode;
cur.next = new ListNode(i);
cur = cur.next;
}
FanzhuanLianBiao fanzhuanLianBiao = new FanzhuanLianBiao();
// cur = fanzhuanLianBiao.reverse(head);
cur = fanzhuanLianBiao.DiGuiReverseList(head);
for (int i = 0; i<10 ; i++){
System.out.print(cur.val+",");
cur=cur.next;
}
}
}
两两交换节点
三个指针,cur指的指针时,能实现后两个节点的两两交换(但实际涉及到后三个节点)。因此首先cur要指向dummyhead。结束时也一样,如果奇数个节点,那么cur.next.next == null 就可以结束了,如果偶数个节点,cur.next == null为空就可以结束了。所以这题关键在于理解起始、终止条件。
package LianBiao;
/**
* 功能:
* 作者:RNG·Cryin
* 日期:2024-07-12 21:03
*/
public class LiangLiangJiaoHuan {
public ListNode solution (ListNode head){
ListNode dummyhead = new ListNode(-1,head);
ListNode cur = dummyhead ;
while(cur.next!=null && cur.next.next!= null){
ListNode node1 = cur.next;
ListNode node2 = cur.next.next;
cur.next = node2;
node1.next = node2.next;
node2.next = node1;
cur = cur.next.next;
}
return dummyhead.next;
}
public static void main(String[] args) {
ListNode head = new ListNode(1);
ListNode cur = head;
for (int i =2; i<= 10; i++ ){
cur.next = new ListNode(i);
cur = cur.next;
}
LiangLiangJiaoHuan liangLiangJiaoHuan = new LiangLiangJiaoHuan();
ListNode newhead = liangLiangJiaoHuan.solution(head);
cur = newhead ;
for (int i =1 ; i<=10; i++){
System.out.print(cur.val+",");
cur = cur.next;
}
}
}
删除倒数第N个元素
快慢指针间隔N+1步,即快慢指针中间有N个节点,当快指针 == null,中间有N个节点,那慢指针就指向了倒数N+1个节点,刚好用来删除第N个节点。
package LianBiao;
/**
* 功能:
* 作者:RNG·Cryin
* 日期:2024-07-15 12:55
*/
public class ShanChuDaoN {
public ListNode solution(ListNode head, int n){
ListNode dummyhead = new ListNode(-1, head);
ListNode fast = dummyhead;
ListNode slow = dummyhead;
for(int i=0 ; i<=n; i++){ //想让快慢指针中间夹着n个节点,那快指针就要走n+1步。
fast = fast.next;
}
while (fast!=null){
fast = fast.next;
slow = slow.next;
}
if (slow.next !=null ) //实际正常的话是不可能为空,只是怕其他地方有错,导致操作了空指针。
slow.next = slow.next.next;
return dummyhead.next;
}
public static void main(String[] args) {
ListNode head = new ListNode(1);
ListNode cur = head;
for (int i =2 ; i<=10 ; i++){
cur.next = new ListNode(i);
cur = cur.next;
}
ShanChuDaoN shanChuDaoN = new ShanChuDaoN();
cur = shanChuDaoN.solution(head,3);
for (int i=1;i<10;i++){
System.out.print(cur.val + ",");
cur = cur.next;
}
}
}
环形链表
总结:利用好x==z这个公式,背住就行了当作已知条件,相遇的点不用你推导公式,if(fast == slow)就能找到了!然后求x的话,利用好x ==z 设置在相遇点和head处分别一个指针,这两个相遇即就是入口处!
package LianBiao;
/**
* 功能:
* 作者:RNG·Cryin
* 日期:2024-07-15 13:39
*/
public class HuanXingLianBiao {
public ListNode solution(ListNode head){
ListNode dummyhead = new ListNode(-1,head);
ListNode slow = new ListNode(0, head);
ListNode fast = new ListNode(0, head);
while(fast!=null && fast.next!=null){
fast = fast.next.next;
slow = slow.next;
if (slow == fast){ //有环
ListNode index1 = new ListNode(0,head);
ListNode index2 = new ListNode(0,head);
while(index1 != index2){
index1 = index1.next;
index2 = index2.next;
}
return index1;
}
}
return null; //到这儿说明是一条直的链表,没有环,当然就没有环入口,所以返回null;
}
public static void main(String[] args) {
ListNode head = new ListNode(1);
ListNode cur = head;
for (int i=2 ; i<=10; i++){
cur.next = new ListNode(i);
cur = cur.next;
}
ListNode innode = head;
for (int i=1 ; i<7; i++){
innode = innode.next;
}
cur.next = innode;
HuanXingLianBiao huanXingLianBiao = new HuanXingLianBiao();
cur =huanXingLianBiao.solution(head);
for (int i =1 ;i<=11; i++){ //多打印一个环入口处
System.out.print(cur.val + ",");
cur = cur.next;
}
}
}