1.第一题:剑指 Offer 24. 反转链表
定义一个函数,输入一个链表的头节点,反转该链表并输出反转后链表的头节点。
示例:
输入: 1->2->3->4->5->NULL
输出: 5->4->3->2->1->NULL
思路:利用前插法进行翻转链表,但是要先设置一个头结点,最后返回头结点的next即可,注意注意,边界值问题。
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode(int x) { val = x; }
* }
*/
class Solution {
public ListNode reverseList(ListNode head) {
if(head==null){ //无节点
return null;
}
else if(head.next==null){ //有一个结点
return head;
}
else{ //有两个及以上结点
ListNode L=new ListNode(0);
L.next=null;
ListNode p=head;
ListNode q;
while(p!=null){
q=p.next;
p.next=L.next;
L.next=p;
p=q;
}
return L.next;
}
}
}
第二题:剑指 Offer 35. 复杂链表的复制
请实现 copyRandomList 函数,复制一个复杂链表。在复杂链表中,每个节点除了有一个 next 指针指向下一个节点,还有一个 random 指针指向链表中的任意节点或者 null。
(1)方法一:传统做法,效果一般
/*
// 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) {
if(head==null){
return null;
}
else{
Node p=head;
Node Head=new Node(p.val);
Node pre=Head;
while(p.next!=null){ //先复制结点和next指针域;
p=p.next;
Node q=new Node(p.val);
pre.next=q;
pre=q;
}
p=Head;
Node q=head;
Node random_p,random_q;
while(p!=null&&q!=null){
random_p=Head;
random_q=head;
while(random_q!=null&&random_p!=null&&random_q!=q.random){
random_q=random_q.next;
random_p=random_p.next;
}
p.random=random_p;
p=p.next;
q=q.next;
}
return Head;
}
}
}
(2)法二:map方法(主要就是考的映射关系本题目)
//利用map(map中存放的是对应原结点和新节点结点之间的映射);
//巧妙地地方在于让原结点相当于是下标,直接找对对应新节点。
class Solution {
public Node copyRandomList(Node head) {
//利用map(map中存放的是对应原结点和新节点结点之间的映射);
//巧妙地地方在于让原结点相当于是下标,直接找对对应新节点。
HashMap<Node,Node> map=new HashMap<>();
Node p=head;
while(p!=null){
map.put(p,new Node(p.val));
p=p.next;
}
p=head;
while(p!=null){
map.get(p).next=map.get(p.next);
map.get(p).random=map.get(p.random);
p=p.next;
}
return map.get(head);
}
}
(3)法三:
原地构建链表的思想,但是需要考虑的指针边界条件很多,千万不要忘了,这种方法很难想,很难想。
class Solution {
public Node copyRandomList(Node head) {
if(head==null){
return head;
}
//原地修改的方法,就是1->2->3变成1->1'->2->2'->3->3',最后再分解;
for(Node p=head;p!=null;p=p.next.next){
Node q=new Node(p.val);
q.next=p.next;
p.next=q;
}
for(Node p=head;p!=null;){
Node temp=p.next;
if(p.random!=null){
temp.random=p.random.next;
}
p=temp.next;
}
Node Head=head.next;
for(Node p=head;p!=null&&p.next!=null;){
Node q=p.next;
p.next=q.next;
p=q;
}
return Head;
}
}
第三题:剑指 Offer 20. 表示数值的字符串
请实现一个函数用来判断字符串是否表示数值(包括整数和小数)。例如,字符串"+100"、“5e2”、"-123"、“3.1416”、"-1E-16"、“0123"都表示数值,但"12e”、“1a3.14”、“1.2.3”、"±5"及"12e+5.4"都不是。
//代码不难,但是要考虑到的情况很多,需要仔细分析每一个字符出现的位置
//比如两端可以有空格但是除此之外都不可以有空格。
//E/e只能出现一次,且之前必须有数字,可以为小数,后面也必须有数字,所以出现过e/E 之后要把numFlag再次设置为0。 //必须为整数。
//+、-可以出现在开始,以及E、e之后的位置,但是其他位置不行。
//’.'也只能出现一次,且必须在e/E的前面,并且之前没出现过‘.’ 。
class Solution {
public boolean isNumber(String s) {
//代码不难,但是要考虑到的情况很多,需要仔细分析每一个字符出现的位置
//比如两端可以有空格但是除此之外都不可以有空格。
//E/e只能出现一次,且之前必须有数字,可以为小数,后面也必须有数字, //必须为整数。
//+、-可以出现在开始,以及E、e之后的位置,但是其他位置不行。
//'.'也只能出现一次,且必须在e/E的前面
s=s.trim();
boolean numFlag=false;
boolean dotFlag=false;
boolean eFlag=false;
for(int i=0;i<s.length();i++){
char ch=s.charAt(i);
if(ch>='0'&&ch<='9'){
numFlag=true;
}
else if(ch=='.'&&!dotFlag&&!eFlag){
dotFlag=true;
}
else if((ch=='e'||ch=='E')&&numFlag&&!eFlag){
eFlag=true;
numFlag=false;
}
else if((ch=='+'||ch=='-')&&(i==0||s.charAt(i-1)=='E'||s.charAt(i-1)=='e')){
}
else{
return false;
}
}
return numFlag;
}
}
第四题4:面试题 02.05. 链表求和
给定两个用链表表示的整数,每个节点包含一个数位。
这些数位是反向存放的,也就是个位排在链表首部。
编写函数对这两个整数求和,并用链表形式返回结果。
示例:
输入:(7 -> 1 -> 6) + (5 -> 9 -> 2),即617 + 295
输出:2 -> 1 -> 9,即912
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode(int x) { val = x; }
* }
*/
class Solution {
public ListNode addTwoNumbers(ListNode l1, ListNode l2) {
ListNode p=l1;
ListNode q=l2;
ListNode R=new ListNode(0);
ListNode cur=R;
int c=0;
if(l1==null&&l2==null){
return null;
}
while(p!=null&&q!=null){
int sum=p.val+q.val+c;
if(sum>=10){
c=1;
}
else{
c=0;
}
p.val=sum%10;
cur.next=p;
cur=p;
p=p.next;
q=q.next;
}
while(p!=null){
int sum=p.val+c;
if(sum>=10){
c=1;
}
else{
c=0;
}
p.val=sum%10;
cur.next=p;
cur=p;
p=p.next;
}
while(q!=null){
int sum=q.val+c;
if(sum>=10){
c=1;
}
else{
c=0;
}
q.val=sum%10;
cur.next=q;
cur=q;
q=q.next;
}
if(c==1){
ListNode temp=new ListNode(1);
cur.next=temp;
c=0;
}
return R.next;
}
}
代码简洁版,但是浪费空间
class Solution {
public ListNode addTwoNumbers(ListNode l1, ListNode l2) {
ListNode p=l1;
ListNode q=l2;
ListNode R=new ListNode(0);
ListNode cur=R;
int c=0;
if(l1==null&&l2==null){
return null;
}
while(p!=null||q!=null){
int num1=(p==null)?0:p.val;
int num2=(q==null)?0:q.val;
int sum=num1+num2+c;
c=sum/10;
cur.next=new ListNode(sum%10);
p=(p==null)? null:p.next;
q=(q==null)? null:q.next;
cur=cur.next;
}
if(c==1){
cur.next=new ListNode(c);
}
return R.next;
}
}
进阶:思考一下,假设这些数位是正向存放的,又该如何解决呢?
示例:
输入:(6 -> 1 -> 7) + (2 -> 9 -> 5),即617 + 295
输出:9 -> 1 -> 2,即912
思路:翻转链表的方法,先遍历两个链表采取前叉法进行翻转之后跟上面一样的运算然后进行结果进行前叉,即再次翻转。