链表系列题
移除元素
题目
给你一个数组 nums 和一个值 val,你需要 原地 移除所有数值等于 val 的元素,并返回移除后数组的新长度。
不要使用额外的数组空间,你必须仅使用 O(1) 额外空间并 原地 修改输入数组。
元素的顺序可以改变。你不需要考虑数组中超出新长度后面的元素。
思路
和移除重复元素是一个道理
代码
public int removeElement(int[] nums,int val){
if(nums.length==0){
return 0;
}
int j=0;
for(int i=0;i<nums.length;i++){
if(nums[i]!=val){
nums[j]=nums[i];
j++;
}
}
return j;
}
方法
无
复杂度
时间复杂度:O(n);空间复杂度:O(1)
合并两个有序链表21
题目
将两个升序链表合并为一个新的 升序 链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。
思路
使用递归的思想,判断链表1的结点和链表2的结点的大小,如果表1的当前结点更大,就传入表2的下一个结点和整个表1,表2大的时候,同理,当有链表为空时,就返回另外一个链表。
方法
无
代码
public ListNode mergeTwoLists(ListNode l1, ListNode l2) {
if(l1==null) return l2;
if(l2==null) return l1;
if(l1.val<l2.val){
l1.next = mergeTwoLists(l1.next,l2);
return l1;
}else{
l2.next = mergeTwoLists(l1,l2.next);
return l2;
}
}
复杂度
时间:N;空间:N;
两数相加(中等题2)
题目
给你两个 非空 的链表,表示两个非负的整数。它们每位数字都是按照 逆序 的方式存储的,并且每个节点只能存储 一位 数字。请你将两个数相加,并以相同形式返回一个表示和的链表。你可以假设除了数字 0 之外,这两个数都不会以 0 开头。
思路
由于输入为逆序,所以两个链表的数字可以直接相加。两个链表当前位置的数分别为n1,n2,进位为carry,则和为n1+n2+carry;其中,答案链表处的位置为(n1+n2+carry)%10,而新进位值为(n1+n2+carry)/10。
如果链表长度不同,可以在短的后面补0。遍历完后,如果carry不为零,则需要新增结点。
方法
无
代码
public ListNode addTwoNumbers(ListNode l1,ListNode l2){
ListNode head = null,tail=null;
int carry=0;
while(l1!=null||l2!=null){
int n1 = (l1==null)?0:l1.val;
int n2 = (l2==null)?0:l2.val;
int sum = n1+n2+carry;
if(head==null){
head = tail = new ListNode(sum%10);
}else{
tail = new ListNode(sum%10);
tail = tail.next;
}
carry=sum/10;
if(l1!=null) l1 = l1.next;
if(l2!=null) l2=l2.next;
}
if(carry>0){
tail.next= new ListNode(carry);
}
return head;
}
复杂度
时间复杂度:O(max(m,n));空间复杂度:O(max(m,n))
删除排序链表中的重复元素
题目
给定一个排序链表,删除所有重复的元素,使得每个元素只出现一次。
思路
使用双指针法,一个指针pre指着前一个结点,一个指针cur指着当前结点。如果pre的值与cur的值相同,就将pre的next指向cur的next;不同就移动pre,每次都要移动cur指针。
方法
无
代码
public ListNode deleteDuplicates(ListNode head) {
if(head==null||head.next==null) return head;
ListNode prev= head,cur=head.next;
while(cur!=null){
if(prev.val==cur.val){
prev.next=cur.next;
}else{
prev=cur;
}
cur=cur.next;
}
return head;
}
复杂度
时间:N;空间:1
61.旋转链表
题目
给定一个链表,旋转链表,将链表每个节点向右移动 k 个位置,其中 k 是非负数。
思路
就是构建一个环,根据k和长度,选择新的头节点。要保证指着的节点就是新的尾节点
方法
无
代码
public ListNode rotateRight(ListNode head, int k) {
if(head==null||head.next==null) return head;
int length = 0;
ListNode cur = head,pre=null;
while(cur!=null){
pre=cur;
cur=cur.next;
length++;
}
int times = k%length;
pre.next=head;
ListNode new_tail = head;
for(int i=0;i<length-times-1;i++){
new_tail=new_tail.next;
}
ListNode new_head = new_tail.next;
new_tail.next=null;
return new_head;
}
复杂度
时间:O(N);空间:1。
82.删除排序链表中的重复元素 2
题目
给定一个排序链表,删除所有含有重复数字的节点,只保留原始链表中 没有重复出现 的数字。
思路
使用三个指针,加上哑节点,pre作为前置节点,slow作为慢节点,fast作为快节点,当slow和fast的值相同时,就移动fast,直至不同为止,pre的next就为fast,但pre不移动,slow的next指向fast,slow移动,不同时,就三个一起动。
方法
无
代码
public ListNode deleteDuplicates(ListNode head) {
if(head==null||head.next==null) return head;
ListNode dummy = new ListNode(0,head);
ListNode pre = dummy,slow = head,fast = head.next;
while(fast!=null){
if(fast.val!=slow.val){
pre=pre.next;
slow=slow.next;
}else{
while(fast!=null&&fast.val==slow.val){
fast=fast.next;
}
pre.next=fast;
slow=fast;
}
if(fast!=null) fast=fast.next;
}
return dummy.next;
}
复杂度
空间:1;时间:N。
86.分隔链表
题目
给你一个链表和一个特定值 x
,请你对链表进行分隔,使得所有小于 x
的节点都出现在大于或等于 x
的节点之前。你应当保留两个分区中每个节点的初始相对位置。
思路
先设置两个头节点,一个为small head,一个为large head,然后设置三个指针,一个为s,一个为l,一个为cur指着head,当cur的值小于x时,s的next就指着cur,s移动,大于时,就是使用l,其余同理。结束后,要将l的next置为空,为了防止l的next不为空又小于x的情况。最后将s的next指向large head的next,合并链表。
代码
public ListNode partition(ListNode head, int x) {
ListNode sH = new ListNode(0);
ListNode lH = new ListNode(0);
ListNode s = sH,l=lH;
ListNode cur = head;
while(cur!=null){
if(cur.val<x){
s.next = cur;
s=s.next;
}else{
l.next = cur;
l=l.next;
}
cur=cur.next;
}
l.next =null;
s.next = lH.next;
return sH.next;
}
复杂度
空间:1;时间:N;