Leetcode 21
题目描述:
将两个升序链表合并为一个新的升序链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。
示例:
输入:1->2->4, 1->3->4
输出:1->1->2->3->4->4
我的思路:
双指针法:
一个指针left1指向l1,一个指针left2指向l2.
此时会遇上四种情况:
①left1《left2,且left1.next<left2
②left1<left2,且left1.next>left2
③left1=left2
④left1>left2
第一种情况:left1=left1.next
第二种情况:left2插入到left1的后面,left1=left1.next
第三种情况:第二种相同
第四种情况:left2插入到left1面前
代码:
/**
* Definition for singly-linked list.
* public class ListNode {
* public int val;
* public ListNode next;
* public ListNode(int x) { val = x; }
* }
*/
public class Solution {
public ListNode MergeTwoLists(ListNode l1, ListNode l2) {
if(l1==null) return l2;
if(l2==null) return l1;
ListNode left3;//在不破坏l2的前提下充当指针
ListNode left1 = l1;//l1的指针,此时left1指向整个链表
ListNode left2 = l2;//l2的指针
while (left1 != null && left2 != null)
{
if(left1.val<left2.val&&left1.next==null)//第五种情况,必须放在第一位,
{ //否则报错
left3=new ListNode(left2.val);
left1.next=left3;
left1=left1.next;
left2=left2.next;
}
else if (left1.val < left2.val && left1.next.val > left2.val)//如果当前小于l2,而下一个大于l2
{
left3 = new ListNode(left2.val);//构建新的结点
left3.next = left1.next;//插入到l1中
left1.next = left3;
left2 = left2.next;//l2往后移
left1 = left1.next;
}
else if(left1.val==left2.val)
{
left3 = new ListNode(left2.val);
left3.next = left1.next;
left1.next = left3;
left2 = left2.next;
left1 = left1.next;
}
else if (left1.val > left2.val)//如果没有比l2小的值
{
left3 = new ListNode(left2.val);//构建新的结点
left3.next = left1;//插入到最前
left1 = left3;
left2=left2.next;
l1=left1; //必须要给l1重新赋值!
}
else left1=left1.next;//第一种情况
}
return l1;
}
}
执行用时 :
112 ms, 在所有 C# 提交中击败了64.44%的用户
内存消耗 :
25.7 MB, 在所有 C# 提交中击败了25.00%的用户
出现的问题:
第四种情况:没有加上left2=left2.next
第一种情况一定要加上,否则四循环
第四种情况:一定要注意在left1前面插入数字时,l1并没有改变
第五种情况:不能使用第二种情况,必须放在第一个!
Leetcode 82
题目描述:
给定一个排序链表,删除所有含有重复数字的节点,只保留原始链表中 没有重复出现 的数字。
示例 1:
输入: 1->2->3->3->4->4->5
输出: 1->2->5
我的思想:
先解决开头,确保开头和开头的下一个是重复数字;
解决中间和结尾:
star记录重复数字的前一个结点,end记录重复数字,然后通过一个函数,返回下一个不重复的结点。
代码:
/**
* Definition for singly-linked list.
* public class ListNode {
* public int val;
* public ListNode next;
* public ListNode(int x) { val = x; }
* }
*/
public class Solution {
public ListNode DeleteDuplicates(ListNode head) {
if(head==null) return null;
head=HeadProcess(head);
if(head==null||head.next==null) return head;
ListNode start=head,end;
end=start.next;
while(end!=null&&end.next!=null)
{
if(end.val==end.next.val)
{end=HeadProcess(end);
start.next=end;
}
else{
start=start.next;
end=end.next;
}
}
return head;
}
ListNode HeadProcess( ListNode head)//将重复的结点输入
{
if(head==null) return null;//递归之后如果为零,则返回
if(head.next==null||head.val!=head.next.val) return head;//递归之后如果下一个不等于
int temp=head.val; //或者没有下一个也返回
while(head!=null&&head.val==temp)//记录重复的值
head=head.next;//开始找下一个
return HeadProcess(head);
}
}
执行用时 :
116 ms, 在所有 C# 提交中击败了51.39%的用户
内存消耗 :
25.8 MB, 在所有 C# 提交中击败了100.00%的用户
Leetcode 61
题目描述:
给定一个链表,旋转链表,将链表每个节点向右移动 k 个位置,其中 k 是非负数。
示例 1:
输入: 1->2->3->4->5->NULL, k = 2
输出: 4->5->1->2->3->NULL
解释:
向右旋转 1 步: 5->1->2->3->4->NULL
向右旋转 2 步: 4->5->1->2->3->NULL
我的思想:构建一个新的链表
首先记录下原有链表的全部的值以及相对应的序号。之后构造字典,计算移位之后的序号,映射原有的值,然后构造开头和结点,从后往前插入。
BUG:list的索引要用【】
代码:
/**
* Definition for singly-linked list.
* public class ListNode {
* public int val;
* public ListNode next;
* public ListNode(int x) { val = x; }
* }
*/
public class Solution {
public ListNode RotateRight(ListNode head, int k) {
if(head==null) return null;
if(k==0) return head;
List<int> list=new List<int>();
Dictionary<int,int> dt=new Dictionary<int,int>();
ListNode targer=head;
while(targer!=null)
{
list.Add(targer.val);//统计值和序号
targer=targer.next;
}
if(list.Count==1) return head;
for(int i=0;i<list.Count;i++)
{
dt[((i+1+k)%(list.Count))]=list[i];//计算移位之后的序号
}
targer=new ListNode(dt[1]);//开头
ListNode tail=new ListNode(dt[0]);//结尾
targer.next=tail;
for(int i=dt.Count-1;i>=2;i--)//从后万千插入
{
tail=new ListNode(dt[i]);
tail.next=targer.next;
targer.next=tail;
}
return targer;
}
}
我的思想二:
不必构造新的链表,只需要得到移位后新的链表的头和尾巴,断开新的链表的头的前面部分,把原来的尾巴与原来的头连接起来即可
代码:
/**
* Definition for singly-linked list.
* public class ListNode {
* public int val;
* public ListNode next;
* public ListNode(int x) { val = x; }
* }
*/
public class Solution {
public ListNode RotateRight(ListNode head, int k) {
if(head==null||head.next==null||k==0) return head;
ListNode orihead=head;//原来的头
ListNode oriend=null;//原来的尾巴
ListNode newend=null;//新的尾巴
ListNode newhead=null;//新的头
int oriendnum,newendnum,newheadnum;
int count=0;//计算长度
while(orihead!=null)
{
orihead=orihead.next;
count++;
}
if(k%count==0) return head;
if(k>count) k=k%count;
orihead=head;
oriendnum=count;//原来的尾巴序号
newendnum=count-k;//新的尾巴序号
newheadnum=count-k+1;//新的头序号
count=1;
while(head!=null)
{
if(oriendnum==count) oriend=head;//原来的尾巴
if(newendnum==count) newend=head;//新的尾巴
if(newheadnum==count) newhead=head;//新的头
count++;
head=head.next;
}
oriend.next=orihead;//原来的尾巴接原来的头
if(newend!=null)
newend.next=null;//新的尾巴后面断开
return newhead;//返回新的头
}
}
执行用时 :
112 ms, 在所有 C# 提交中击败了61.39%的用户
内存消耗 :
25.3 MB, 在所有 C# 提交中击败了100.00%的用户
BUG:count一开始不能设置为1,否则会多一个,因为你的while跳出条件为null,所以最后head=null,count也多算一个。
Leetcode 92
题目描述:
反转从位置 m 到 n 的链表。请使用一趟扫描完成反转。
说明:
1 ≤ m ≤ n ≤ 链表长度。
示例:
输入: 1->2->3->4->5->NULL, m = 2, n = 4
输出: 1->4->3->2->5->NULL
我的思想:
因为是一趟反转,所以必然使用while和count进行计数。
要设置五个指针:①指向原始的头用于返回②用于指向反转链表的第一个③用于反转中指向下一个④用于反转中被指向⑤用于指向反转链表的前一个
代码:
/**
* Definition for singly-linked list.
* public class ListNode {
* public int val;
* public ListNode next;
* public ListNode(int x) { val = x; }
* }
*/
public class Solution {
public ListNode ReverseBetween(ListNode head, int m, int n) {
if(head==null||head.next==null||m==n) return head;//当m=n是while里面全为空
int count=0;
ListNode truestart=head;//用于返回
ListNode First=null,attack=null,defense=null;
ListNode start=null;//反转链表的头一个
while(head!=null&&count<=n)//因为n有可能等于count,所以count不能等于n+1
{
count++;
if(count<m-1) head=head.next;
else if(count==m-1) { start=head; head=head.next;}
else if(count==m) {First=head; defense=First; head=head.next;}//第一个发转的头
//First.next=null会使得后面全为空
else if(m<count&&count<=n)
{
attack=head;
head=head.next;
attack.next=defense;
defense=attack;
}
}
First.next=head;
if(m==1) return defense;//如果m=1,star会等于null,后面插入的不在truestart后面
else { start.next=defense;return truestart;}
}
}
执行用时 :
96 ms, 在所有 C# 提交中击败了98.11%
的用户
内存消耗 :
24.3 MB, 在所有 C# 提交中击败了100.00%的用户
Leetcode 2
题目描述:
给出两个 非空 的链表用来表示两个非负的整数。其中,它们各自的位数是按照 逆序 的方式存储的,并且它们的每个节点只能存储 一位 数字。
如果,我们将这两个数相加起来,则会返回一个新的链表来表示它们的和。
您可以假设除了数字 0 之外,这两个数都不会以 0 开头。
示例:
输入:(2 -> 4 -> 3) + (5 -> 6 -> 4)
输出:7 -> 0 -> 8
原因:342 + 465 = 807
我的思路:从两条链表中逐个去除数字,相加它们的值。%10存储到新的链表,/10加到任意一个原来的链表。如果原来的链表的下一个为空,则加到temp,并且跳出循环,然后对于长的链表再做一次加法。
代码:
/**
* Definition for singly-linked list.
* public class ListNode {
* public int val;
* public ListNode next;
* public ListNode(int x) { val = x; }
* }
*/
public class Solution {
public ListNode AddTwoNumbers(ListNode l1, ListNode l2) {
ListNode first=l1,second=l2;
ListNode result=null;
ListNode end=null,num=null;
int temp=0;
while(first!=null&&second!=null)
{
num=new ListNode((first.val+second.val)%10);
if(end!=null)
{end.next=num;
end=end.next;
}
else {result=num;end=result;}//初始赋值!!
if(first.next!=null&&second.next!=null)//下一个还有的情况
{
first.next.val+=(first.val+second.val)/10;
}
else temp=(first.val+second.val)/10;//包括三种情况:长度一样,l1长,l2长
first=first.next;
second=second.next;
}
if(first==null&&second==null)
{ if(temp==0) return result;
else
{ num=new ListNode(temp);end.next=num; return result; }
}
else if(first!=null&&second==null) { end.next=Process(first,temp); }
else if(first==null&&second!=null) {end.next=Process(second,temp);}
return result;
}
ListNode Process(ListNode head,int temp)
{
ListNode start=head;
head.val+=temp;
int test=0;
if(head.val>=10)
{
while(head.val>=10)
{
test=head.val;
head.val=test%10;
if(head.next==null){ ListNode k=new ListNode(test/10);head.next=k; }//要注意next可能会为空
else head.next.val+=test/10;
head=head.next;
}
}
else {return start;}
return start;//BUG:一开始返回head,但是head推移之后会少了之前的数字,测试用例【1】和【9,9,】会出错
}
}
执行用时 :
124 ms, 在所有 C# 提交中击败了83.44%的用户
内存消耗 :
27.7 MB, 在所有 C# 提交中击败了11.11%的用户
Leetcode 147
题目描述:
对链表进行插入排序。
示例 1:
输入: 4->2->1->3
输出: 1->2->3->4
代码:
/**
* Definition for singly-linked list.
* public class ListNode {
* public int val;
* public ListNode next;
* public ListNode(int x) { val = x; }
* }
*/
public class Solution {
public ListNode InsertionSortList(ListNode head) {
if(head==null||head.next==null) return head;
ListNode result=head;//结果
ListNode start=head.next;//未排序的开头
ListNode target;//待排序的目标
ListNode end=head;//已排序的结尾
ListNode num;//插入的位置
while(start!=null)
{
target=start;
start=start.next;
if(target.val<end.val)
{
num=result; //一开始把用head代替了result!
if(target.val<=num.val){ target.next=num; result=target; }
else
{
while(num.val<target.val)
{ //一开始每加下面这一步
if(num.next.val>=target.val) { target.next=num.next;num.next=target; }
num=num.next;
}
}
end.next=start;//打破循环的关键代码
}
else if(end.next!=null) end=end.next;
}
return result;
}
}
执行用时 :
112 ms, 在所有 C# 提交中击败了95.92%的用户
内存消耗 :
25.4 MB, 在所有 C# 提交中击败了100.00%的用户
Offer 24反转两个链表
题目:定义一个函数,输入一个链表的头节点,反转该链表并输出反转后链表的头节点。
示例:
输入: 1->2->3->4->5->NULL
输出: 5->4->3->2->1->NULL
限制:
0 <= 节点个数 <= 5000
我的思路:
①.用两个节点,一个指向原链表第一个,一个指向下一个,当下一个不为零时,用下一个指向上一个。
代码:
public ListNode ReverseList(ListNode head) {
if(head==null) return null;
ListNode p1; ListNode p2=new ListNode(0);
while(head.next!=null)
{
p1=new ListNode(head.val);
p2=new ListNode(head.next.val);
p2.next=p1;
head=head.next;
}
return p2;
}
出现的BUG:只输出了两个节点,因为p1,p2每次都是重新断开连接!