链表
链表与数组
链表和数组作为算法中的两个基本数据结构,在算法设计是基础的.
数组在物理存储单元中是连续和线性的,得益于物理存储中的优势,数组在索引数据时的时间复杂度可以达到O(1),本质上为内存的偏移定向,而当要删除或添加元素时时间复杂度会达到O(n)
链表在物理存储单元中是非连续非线性的,可以很好的运用内存中的碎片空间.在删除和添加结点上时间复杂度为O(1),而当查找时时间复杂度为O(n)
1.链表逆序 LeetCode 206
反转一个单链表。
示例:
输入: 1->2->3->4->5->NULL 输出: 5->4->3->2->1->NULL
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode(int x) { val = x; }
* }
*/
class Solution {
public ListNode reverseList(ListNode head) {
ListNode newHead=null; //新的链表
while(head!=null){
ListNode newNext=head.next;//备份 newNext 2->3->4->5->NULL
head.next=newHead; //翻转操作 原子结点成为原父节点的父节点 head.next -> null
newHead=head;//移位 newHead 接受不断传下来的head头节点 newHeda 1->null
head=newNext;//不断得到迭代 把原来的链表一个个取出来 head 2->3->4->5->null
}
return newHead;
}
}
内存消耗超过97%
2.链表逆序b LeetCode 92
反转从位置 m 到 n 的链表。请使用一趟扫描完成反转。
说明: 1 ≤ m ≤ n ≤ 链表长度。
示例:
输入: 1->2->3->4->5->NULL, m = 2, n = 4 输出: 1->4->3->2->5->NULL
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode(int x) { val = x; }
* }
*/
class Solution {
public ListNode reverseBetween(ListNode head, int m, int n) {
int change_len=n-m+1; //要交换节点的次数
ListNode preHead=null;
ListNode result=head;
ListNode newHead=null;
while(head!=null && --m!=0){ //将链表后移到要交换的位置
preHead=head;
head=head.next;
}
ListNode modify_tail=head; //原来的头指针
while(change_len!=0 && head!=null ){
ListNode newNext=head.next; //备份head.next
head.next=newHead; //翻转
newHead=head; //继承
head=newNext; //迭代
change_len--; //记录要交换的次数
}
modify_tail.next=head; //重新将指针指向现在的头节点
if(preHead!=null){ //一般情况
preHead.next=newHead;
}else{ //处理直接从起始位置交换的情况
result=newHead;
}
return result;
}
}
内存消耗超过94%
3.链表求环 Leetcode 142
给定一个链表,返回链表开始入环的第一个节点。 如果链表无环,则返回 null。
为了表示给定链表中的环,我们使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。 如果 pos 是
-1,则在该链表中没有环。注意,pos 仅仅是用于标识环的情况,并不会作为参数传递到函数中。说明:不允许修改给定的链表
示例 1:
输入:head = [3,2,0,-4], pos = 1 输出:返回索引为 1 的链表节点 解释:链表中有一个环,其尾部连接到第二个节点
快慢指针法
/**
* Definition for singly-linked list.
* class ListNode {
* int val;
* ListNode next;
* ListNode(int x) {
* val = x;
* next = null;
* }
* }
*/
public class Solution {
public ListNode detectCycle(ListNode head) {
ListNode fast=head;
ListNode slow=head;
ListNode meet=null;
while(fast!=null){
fast=fast.next;
slow=slow.next;
if(fast!=null){ //多走一步
fast=fast.next;
}
if(fast==slow){
meet=fast; //记录meet位置
break;
}
}
if(meet==null){
return null;
}
while(meet!=null && head!=null){
if(head==meet){
return meet;
}
head=head.next; //两边同时前进相同的步数将会在 环的起始位置相遇
meet=meet.next;
}
return null;
}
}
执行用时超100%,内存消耗超95%
4.链表划分 LeetCode 138
给定一个链表和一个特定值 x,对链表进行分隔,使得所有小于 x 的节点都在大于或等于 x 的节点之前。
你应当保留两个分区中每个节点的初始相对位置。
示例:
输入: head = 1->4->3->2->5->2, x = 3 输出: 1->2->2->4->3->5
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode(int x) { val = x; }
* }
*/
class Solution {
public ListNode partition(ListNode head, int x) {
ListNode lessHead=new ListNode(0); //小于的一部分
ListNode moreHead=new ListNode(0);//大与的一部分
ListNode lessPtr=lessHead;
ListNode morePtr=moreHead;
while(head!=null){
if(head.val<x){
lessPtr.next=head; //小于的一个提出来放到小于部分里的
lessPtr=lessPtr.next;
}else{
morePtr.next=head; //大于的一个提出来放到大于部分里的
morePtr=morePtr.next;
}
head=head.next; //判别下一个
}
lessPtr.next=moreHead.next; //合并两个部分
morePtr.next=null; //置空
return lessHead.next;
}
}
内存消耗超95%
链表深度拷贝 LeetCode 138
给定一个链表,每个节点包含一个额外增加的随机指针,该指针可以指向链表中的任何节点或空节点。
要求返回这个链表的 深拷贝。
我们用一个由 n 个节点组成的链表来表示输入/输出中的链表。每个节点用一个 [val, random_index] 表示:
val:一个表示 Node.val 的整数。 random_index:随机指针指向的节点索引(范围从 0 到
n-1);如果不指向任何节点,则为 null
示例1
输入:head = [[7,null],[13,0],[11,4],[10,2],[1,0]]
输出:[[7,null],[13,0],[11,4],[10,2],[1,0]
/*
// Definition for a Node.
class Node {
int val;
Node next;
Node random;
public Node(int val) {
this.val = val;
this.next = null;
this.random = null;
}
}
*/
class Solution {
public Node copyRandomList(Node head) {
Map<Node,Integer> map1 =new HashMap<Node,Integer>(); //通过存储结点map来获取他所在的索引位置
ArrayList<Node> vec=new ArrayList<Node>(); //遍历新的链表
Node ptr=head; //操作指针
int i=0;
while(ptr!=null){
vec.add(new Node(ptr.val)); //新建Node并放入链表中
map1.put(ptr,i); //存储在map中
ptr=ptr.next; //下一个操作结点
i++;
}
vec.add(null); //边界操作,当要遍历最后一个结点时仍然适用
ptr=head;
i=0;
while(ptr!=null){
vec.get(i).next=vec.get(i+1); //结点的链接
if(ptr.random!=null){ //若随机指引结点不为空执行链接
int id =map1.get(ptr.random); //获取随机链表的索引id
vec.get(i).random=vec.get(id); //指引结点
}
i++;
ptr=ptr.next; //下一个操作结点
}
return vec.get(0);
}
}
总结
以上是LeetCode中经典链表题的解答致谢:小象学院leetcode刷题教程