从0-1学习数据结构与算法--链表篇

链表

1,线性表

线性存储结构 一根线能串起来的数组 存储到物理空间中

数据需要有相同的数据类型

元素直接的关系 需要是一对一

分为有头结点的链表和没有头结点的链表

插入的时候 分为头插法和尾插法

节点的关系 称之为前置节点和后继节点的引用

2,链表中倒数第k个节点

分析:

解法1》

先遍历出链表的总长度n 倒数第k个节点=从头遍历的第n-k+1个节点

public static ListNode1 getKthFromEnd(ListNode1 head , int k){

int n = 0;

//先求出链表总长度

ListNode1 tmp = head;

while(tmp.next != null){

n++;

tmp = tmp.next;

}

//再次遍历 找到第n-k+1个节点

tmp = head;

for (int i = 0; i < n-k+1; i++) {

tmp = tmp.next;

}

return tmp;

}

解法2》

先定义额外指针 找到正数第K个节点

两个指针同事向右移动 当快的指针到达链表最后一个节点时

慢的指针 正好到达倒数第k个节点(相当于慢的指针没走的k步 由快的指针帮忙走完了

public static ListNode1 getKthFromEnd1(ListNode1 head , int k){

//快慢指针

ListNode1 slow = head;

ListNode1 fast = head;

//正数第k个节点

for (int i = 1; i < k; i++) {

fast = fast.next;

}

System.out.println(fast.val);

while(fast.next != null){

slow = slow.next;

fast = fast.next;

System.out.println("slow移动到"+slow.val+",fast移动到"+fast.val);

}

return slow;

}

3. 反转链表

让一个节点的next指向前置节点 2-》1

让元next节点指向自身3-》2

给你单链表的头节点 head ,请你反转链表,并返回反转后的链表。

示例 1:

输入:head = [1,2,3,4,5]输出:[5,4,3,2,1]

示例 2:

输入:head = [1,2]输出:[2,1]

示例 3:

输入:head = []输出:[]

在引用更改时,会丢失对原next节点的引用,需要用临时的指针tmp记录一下

public static ListNode1 reverseList(ListNode1 head) {

//记录前置节点和当前节点

ListNode1 pre = null;

ListNode1 cur = head;

while (cur != null){

ListNode1 tmp = cur.next;

//更改指针

cur.next = pre;

//pre和cur向后移动

pre = cur;

cur = tmp;

}

return pre;

}

4,双链表

1,链表与数组区别

优点:链表实现了真正的动态,不需要处理固定容量带来的系列问题

缺点:失去随机访问的能力

链表增删快,查询慢;数组查询快,增删慢

2,结构

单链表 数据+下一个节点的引用

双链表 数据+下一个节点的引用+对上一个节点的引用

java的经典实现--LinkedList

查询方式

根据元素索引位置找到元素

5. 环形链表

1》判断是否有环

给定一个链表,判断链表中是否有环。

如果链表中有某个节点,可以通过连续跟踪 next 指针再次到达,则链表中存在环。 为了表示给定链表中的环,我们使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。 如果 pos-1,则在该链表中没有环。注意:pos 不作为参数进行传递,仅仅是为了标识链表的实际情况。

如果链表中存在环,则返回 true 。 否则,返回 false

进阶:

你能用 O(1)(即,常量)内存解决此问题吗?

示例 1:

输入:head = [3,2,0,-4], pos = 1输出:true解释:链表中有一个环,其尾部连接到第二个节点。

示例 2:

输入:head = [1,2], pos = 0输出:true解释:链表中有一个环,其尾部连接到第一个节点。

示例 3:

输入:head = [1], pos = -1输出:false解释:链表中没有环。

分析:

环形链表 其实是形成了一个圈

跑步 快的人和慢的人在某一个点相遇 这能倒推出跑道是环形的

“快慢指针” 构造一个遍历时 走一步的指针 为慢指针

public static boolean hasCycle(ListNode1 head){

//如果链表本身不能为空 或者只有一个节点也是五环的

if(head == null || head.next == null)

return false;

ListNode1 slow = head;

ListNode1 fast = head;

while (true){

//没有环时 fast会先走到链表尾部

if(fast == null || fast.next == null){

return false;

}

slow = slow.next;

fast = fast.next.next;

if(slow == fast){

return true;

}

}

}

2》. 求环的入口节点

给定一个链表,返回链表开始入环的第一个节点。 如果链表无环,则返回 null

为了表示给定链表中的环,我们使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。 如果 pos-1,则在该链表中没有环。注意,pos 仅仅是用于标识环的情况,并不会作为参数传递到函数中。

说明:不允许修改给定的链表。

进阶:

  • 你是否可以使用 O(1) 空间解决此题?

示例 1:

输入:head = [3,2,0,-4], pos = 1输出:返回索引为 1 的链表节点解释:链表中有一个环,其尾部连接到第二个节点。

示例 2:

输入:head = [1,2], pos = 0输出:返回索引为 0 的链表节点解释:链表中有一个环,其尾部连接到第一个节点。

示例 3:

输入:head = [1], pos = -1输出:返回 null解释:链表中没有环。

分析:

快慢指针

第一次相遇之后,让fast重新指向头结点head,slow保持不变,fast和slow按照相同的速度移动,第二次相遇后,此节点即为入口节点。

【3,2,0,-4】

Slow 3 2 0 -4 走了三部

Fast 3 0 2 -4 走了六部(是slow的2倍)

fast-slow的步数 其实是环的长度

因为头结点 途径的路程 是从头结点除非 经过入口节点 到达相遇点的

头结点到入口节点的距离为一步

因为环的长度已知

所以从相遇再行进多声部 能回到入口节点= 环的长度-相遇点距离入口节点的长度

public static ListNode1 detectCycle(ListNode1 head) {

//如果链表本身不能为空 或者只有一个节点也是五环的

if(head == null || head.next == null)

return null;

ListNode1 slow = head;

ListNode1 fast = head;

while (true){

//没有环时 fast会先走到链表尾部

if(fast == null || fast.next == null){

return null;

}

slow = slow.next;

fast = fast.next.next;

if(slow != null){

System.out.print("slow 走到"+slow.val);

}

if(fast != null){

System.out.println(",fast走到"+fast.val);

}

if(slow == fast){

break;

}

}

//第一次相遇之后,让fast重新指向头结点head,slow保持不变,fast和slow按照相同的速度移动,第二次相遇后,此节点即为入口节点。

fast = head;

while (true){

slow = slow.next;

fast = fast.next;

if(slow != null){

System.out.print("slow走到"+slow.val);

}

if(fast != null){

System.out.println(",fast走到"+fast.val);

}

if(slow == fast)

return slow;

}

}

3》约瑟夫环

有N个人围成一圈,从第一个人开始报数,报道M被杀,然后下一个人继续从1开始报数,循环往复,直到剩下最后一个,最后一个人的初始位置在哪里?

分析:

1》环形链表法

将每个人试做链表中的一个节点,当报数到m的时候,删除节点,直到剩下最后一个节点

当找到报数m-1的节点 node node.next = node.next.next;

当只剩最后一个节点时候 node.next = node;

public static int josephus(int n, int m){

//初始化环形 链表

int[] arr = new int[n];

for (int i = 0; i < arr.length; i++) {

arr[i] = i + 1;

}

ListNode1 node1 = ListNode1.arrayToListNode(arr);

ListNode1.toCycle(node1,0);

/**

*将每个人试做链表中的一个节点,当报数到m的时候,删除节点,直到剩下最后一个节点

* 当找到报数m-1的节点 node node.next = node.next.next;

* 当只剩最后一个节点时候 node.next = node;

**/

int cnt = 1;

while (node1.next != node1){

if(cnt == m-1){

System.out.println("删除节点"+node1.next.val);

node1.next = node1.next.next;

cnt = 0;

}

node1 = node1.next;

cnt++;

if(node1.next == node1)

return node1.val;

}

return 0;

}

2》数据遍历法

将每个人视作数组中的一个元素,当报数到m的时候,使用占位符代表此人撕掉了

当剩余人数 只有一个人时候 停止循环

public static int josephus1(int n,int m){

//数组记录 初始是0 使用-1代表当前元素 死掉

int[] people = new int[n];

//人的索引

int index = -1;

//报数1 2 .....m

int cnt = 0;

//剩余人数

int remain = n;

while (remain > 0){

index++;

//到达数组末端 重新从头遍历

if(index >= n)

index = 0;

//如果此人撕掉 跳过 继续报数

if(people[index] == -1){

continue;

}

//报数到m 将index对应位置的元素 职位-1 (尸体)

if(cnt == m){

people[index] = -1;

cnt = 0;

remain--;

}

cnt++;

}

return index;

}

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

JAVA终结者

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值