第一个圈 ListNode相当于之前的Node 一个引用类 到之后形成一个节点
第二个圈 定义了两个域 相当于之前的data和next
第三个是 一个构造方法 便于之后用 域
ListNode表示一个返回值
1.移除链表元素
代码如下:
public class Eriksenn{
public class ListNode{
int val;
ListNode next;
ListNode(int x){
val=x;
}
class Soulution{
//返回值类型为ListNode 为引用类型 应当返回指针引用类型
public ListNode removeElements(ListNode head,int val){
if(head==null){
return null;
}
//定义出两个要用的箭头标志
// 由于head已经由ListNode传入所以不能用养成习惯的this
ListNode prev=head;
ListNode cur=prev.next;
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;
}
//返回值为引用类型 head指向也表示引用类型
return head;
}
}
}
}
}
2.反转链表
本题思路说难确实 要反转一个链表那就创三个引用对象指向
分别为 prev cur curNext
注意:
1.初始时与进行过程中 他们三个的相对位置关系是不变的
相对位置 第一位为prev 二 cur 三 curNext
2.在实现过程 注意先变化prev的位置关系 再去变化cur的 最后变化curNext、
值得一提的是在变化过程中 他们三个的相对位置一直是不发生变化的
代码如下
public class Eriksenn {
public class ListNode{
int val;
ListNode next;
ListNode(int x) {
val = x;
}
public ListNode resevre(ListNode head,ListNode newHead) {
//初始化时cur指向头 其他两个都可能是null
// 所以都初始化为null
ListNode newhead=new ListNode(1);
ListNode prev = null;
ListNode cur = head;
ListNode curNext = null;
//为什么用cur做判断条件
// 因为curNext为空时还有元素没有搞好
//有可能head一开始为空
while (cur != null) {
curNext = cur.next;
if (curNext == null) {
//反转后的头就是cur
newHead = cur;
}
//举个例子当一开始时prev为null
// cur指向头这一波下来直接把头的next给赋成null 进而继续
cur.next = prev;
//按照规律先变化prev 再cur 再上去变化curNext
prev = cur;//prev往前移一个
cur = cur.next;
//只要cur不为null 那么继续往上变化curNext
}
return newhead;//返回新的头即可
}
}
}
3.链表的中间结点
核心在其中
代码如下
public class Eriksenn {
public class ListNode {
int val;
ListNode next;
ListNode(int x) {
val = x;
}
public ListNode findmid(ListNode head, ListNode newHead) {
//给出两个指向 fast是slow移动的二倍
ListNode slow=head;
ListNode fast=head;
if(head==null){
return slow;
}
//不可调换位置 若先fast.next为空
// 由于fast已经为空了
// 然后fast.next空的空 则会空指针异常
//这两种情况是根据总结点数是奇数或偶数给出的 自己画一下
//记住核心 slow每次走一步 fast走两步
while(fast!=null&&fast.next!=null){
//fast走两步
fast=fast.next.next;
slow=slow.next;
}
return slow;
}
}
}
public class Eriksenn {
class Node{
public Node next;
public Node head;
public Node yidong(int k){
Node fast=this.head;
Node slow=this.head;
while(k-1>0){
if(fast.next!=null){
fast=fast.next;
k--;
} else {
System.out.println("没有这个节点");
}
}
if(fast.next!=null){
fast=fast.next;
slow=slow.next;
}
return slow;
}
}
}
思路 如果选取一个数据data为12 首先开出两块空间
那么根据题目要求得知 小于12的 放前面一块 大于12的 放后面一块
用尾插法进行放置
之后再把他俩块连在一起
public Node part(int x){
Node bs=null;
Node be=null;
Node as=null;
Node ae=null;
Node cur=this.head;
while(cur!=null){
//当原链表cur指向数据小于x 放到第一段
if(cur.data<x){
//当第一次时 bs指向null
if(bs==null){
//bs be 都应该指向cur
bs=cur;
be=cur;
//如果bs不为空 那么用尾插法进行
} else {
be.next=cur;
be=be.next;
}
//cur指向data 大于x时 放到第二段
} else {
//第一次时as指向null
if(as==null){
//as ae 都应该指向cur
as=cur;
ae=cur;
} else {
ae.next=cur;
ae=ae.next;
}
}
cur=cur.next;
}
//1.判断bs是否为空
if(bs==null){
return as;
}
//2.判断bs不为空 需要进行拼接
be.next=as;
//3.如果ae不为空 ae的next需要置为空
if(ae!=null){
ae.next=null;
}
//拼接之后返回bs
return bs;
}
}
}
public Node leomessi(){
//创建一个虚拟头来链接不重复的节点
//默认为空的
Node node=new Node(-1);//定义时随意给data值
Node cur=this.head;
Node tmp=node;
while(cur!=null){
//因为空的data取出来肯定会发生空的异常
if(cur.next!=null&&cur.data==cur.next.data){
//由于连着重复的数据有可能不止有两个 定义一个while循环
while(cur.next!=null&&cur.data==cur.next.data){
cur=cur.next;
}
//当走到最后一个重复时 得多走一步
cur=cur.next;
} else {
//为了不让node这个头动 那么定义一个tmp代替node
tmp.next=cur;
tmp=tmp.next;
cur=cur.next;
}
}
//把最后一个节点的next置为空 防止死循环
tmp.next=null;
//每一次都返回头的next node一直随着tmp的移动而变化的
return node.next;
}
}
}
回文结构即是数据从前往后读 和 从后往前读 都是相同的
public boolean chkmid(){
if(this.head==null){
return false;
}
if(this.head.next==null){
return true;
}
//1.找到单链表的中间结点
Node fast=this.head;
Node slow=this.head;
while(fast!=null||fast.next!=null){
fast=fast.next.next;
slow=slow.next;
}
//2.反转单链表的后半部分 slow肯定在中间位置
Node cur=slow.next;
while(cur!=null){
Node curNext=cur.next;
cur.next=slow;
slow=cur;
cur=curNext;
}
//slow此时为最后一个节点了
//3.一个从前往后走 一个从后往前走 看过程中是否有不相同的
//当这两个指针不相遇时
while(this.head!=slow){
if(this.head.data!=slow.data){
return false;
}
//当链表中的个数为偶数时 这种情况存在
if(this.head.next==slow){
return true;
}
this.head=head.next;
slow=slow.next;
}
return true;
}
public boolean sss(){
Node fast=this.head;
Node slow=this.head;
while(fast!=null&&fast.next!=null){
fast=fast.next.next;
slow=slow.next;
//说明他俩相遇
if(slow==fast){
return true;
}
//当存在null时说明无环
if (fast == null || fast.next == null) {
return false;
}
}
return false;
}
public Node sss() {
Node fast = this.head;
Node slow = this.head;
while (fast != null || fast.next != null) {
fast = fast.next.next;
slow = slow.next;
//说明他俩相遇
if (slow == fast) {
break;
}
}
//当存在null时说明无环 即没有存在第一个入口点
//它是因为fast 或fast.next为null时才退出
if (fast == null || fast.next == null) {
return null;
}
//不满足上面条件退出 证明是存在环的
//那么此时我们 把slow指向头 slow与fast一步一步走
//fast slow一步一走的相遇点即是第一次的入口点
slow = this.head;
while(fast!=slow){
fast=fast.next;
slow=slow.next;
}
return fast;//或return slow;都一样
}
public Node lionelmessi(Node headA,Node headB) {
//先假设出pl指向长的单链表 ps指向短的
//如果到后面求差值时发现不正确 那么进行调换 反正规定pl指向长的 ps指向短的
Node pl = headA;
Node ps = headB;
int lenA=0;
int lenB=0;
while (pl != null) {
pl=pl.next;
lenA++;
}
while(ps!=null){
ps=ps.next;
lenB++;
}
//求出两链表差值
int len=lenA-lenB;
pl=headA;
ps=headB;
//证明最长的是ps 而不是pl
if(len<0){
pl=headB;
ps=headA;
}
//先让较长的一条 我们规定是pl指向最长的
// pl先走差值步 保证他俩在一条起跑线
//再到之后pl ps各走一步 进行判断是否有公共点
for(int i=0;i<len;i++){
pl=pl.next;
}
while(pl!=null&&ps!=null&&ps!=pl){
pl=pl.next;
ps=ps.next;
}
if(pl==ps&&pl!=null&&ps!=null){
return ps;
}
return null;
}
}
public Node neymar(Node headA,Node headB){
//1.首先申请一个虚拟节点 用作为合并后的头 把两个链表接到后面
Node newhead=new Node(1);
//2.设定headA headB分别指向两个链表
//3.比较两个链表的数据 把较小的那一个接到newhead的后面
//由于newhead不可发生改变所以 创建一个变量为tmp用来指向这个新头
Node tmp=newhead;
if(headA.data<headB.data){
tmp.next=headA;
tmp=tmp.next;
headA=headA.next;
} else {
tmp.next=headB;
tmp=tmp.next;
headB=headB.next;
}
//4.当一个链条为空时 把另一个直接接到tmp后面
//由于两个链表是相同长度的 所以只要接一次
if(headA!=null){
tmp.next=headA;
}
if(headB!=null){
tmp.next=headB;
}
return newhead;
}
}