今天是秋招预备队算法篇第九天,今天的目标是两道题,必须完成!!
问题1:判断一个链表是否为回文结构
描述:
解题方法:
想要判断链表是否为回文结构,那么就需要比较链表头从头遍历和链表尾从尾遍历依次得到的结点值是否相等,而单链表是无法直接从尾反向遍历的,所以我们可以将链表反转,然后比较原链表和反转链表结点值,当然我们也可以借助数组,利用数组下标可以双向遍历,比较结点值,判断是否为回文结构
1、数组(双指针遍历)
1)遍历链表,将链表结点值存入数组
2)利用双指针遍历数组,左指针从左遍历,右指针从右遍历,并且比较元素值
3)重复遍历,直到左指针的下标大于右指针,即两者相遇
4)若元素相等,则是回文结构,反之,则不是
import java.util.*;
/*
* public class ListNode {
* int val;
* ListNode next = null;
* }
*/
public class Solution {
/**
*
* @param head ListNode类 the head
* @return bool布尔型
*/
public boolean isPail (ListNode head) {
// write code here
if(head == null){
return false;
}
//数组
ArrayList<Integer> lists = new ArrayList<>();
//遍历链表,将结点值存入数组
while(head != null){
lists.add(head.val);
head = head.next;
}
//双指针
int left = 0;
int right = lists.size() - 1;
//双指针遍历数组
while(left <= right){
//双指针元素值不相等返回false
int l = lists.get(left);
int r = lists.get(right);
//注意,这里要转换为数值类型,不然比较的是Integer对象地址
if((int)lists.get(left) != (int)lists.get(right)){
return false;
}
left++;
right--;
}
//正反序相等
return true;
}
}
时间复杂度:O(n),其中n为链表的长度,遍历链表转化数组为O(n),双指针遍历半个数组为O(n)
空间复杂度:O(n),记录链表元素的辅助数组
2、反转链表(快慢指针)(推荐使用)
我们比较元素值,只需要比较链表前后两部分,不需要将整个链表进行对比,所以我们仅需要将链表从中间分开,反转后半部分就可以比较前后两部分的元素值了,反转链表可以使用栈和双指针这两种方式,在此仅使用双指针反转链表
1)使用快慢指针遍历链表,快指针走两步,慢指针走一步
2)当快指针到达链表尾时,慢指针刚好位于链表中间
3)将链表从慢指针位置分开,反转后半部分
4)比较前后部分的结点值
5)若元素相等,则是回文结构,反之,则不是
import java.util.*;
/*
* public class ListNode {
* int val;
* ListNode next = null;
* }
*/
public class Solution {
/**
*
* @param head ListNode类 the head
* @return bool布尔型
*/
public boolean isPail (ListNode head) {
if(head == null){
return false;
}
//快慢指针
ListNode slow = head;
ListNode fast = head;
//遍历链表,找到中间结点
while(fast != null && fast.next != null){
slow = slow.next;
fast = fast.next.next;
}
//反转链表后半部分
fast = reverseList(slow);
slow = head;
//现在链表已经分为两部分
//若链表结点为奇数,则两段链表结点个数一致,并在中间结点相交
//如:12321 slow指向3 前一部分:123 后一部分 123 在3相交
//若链表结点为偶数,则后半部分链表结点个数比前一部分少一个,在n/2+1个结点处相交
//如:1221 slow指向第二个2 前一部分:122 后一部分:12 相交于第二个2
//所以只需要以后半部分链表为基础,比较两段链表的结点值,判断是否为回文结构
while(fast != null){
//链表结点值不等
if(fast.val != slow.val){
return false;
}
slow = slow.next;
fast = fast.next;
}
return true;
}
//链表反转函数
public ListNode reverseList(ListNode head){
ListNode pre = null;
while(head != null){
ListNode tmp = head.next;
head.next = pre;
pre = head;
head = tmp;
}
return pre;
}
}
时间复杂度:O(n),其中n为链表的长度,双指针找到中点遍历半个链表,后续反转链表为O(n),然后再遍历两份半个链表
空间复杂度:O(1),常数级变量,没有额外辅助空间
问题2:链表的奇偶重排
描述:
解题方法:
1、双指针
利用左右指针遍历链表,左指针指向头节点,右指针指向头节点的下一结点,每次移动两步,则左指针依次遍历链表的奇数结点,右指针依次遍历链表的偶数结点,将奇偶节点分别组成一个链表,遍历结束后将两个链表合并,即可得到奇偶重排的链表
1)创建左右双指针,左右指针分别指向头节点和头节点的下一节点
2)设置两个虚拟头节点,其下一节点分别为头节点和头节点的下一节点,分别代表奇数节点链表和偶数节点链表
3)左右指针每次走两步,遍历链表,组成奇数节点链表和偶数节点链表
4)直至右指针遍历到链表尾结点(链表节点数为偶数)或者右指针为空(链表节点数为奇数),遍历结束
5)将奇数节点链表和偶数节点链表合并
import java.util.*;
/*
* public class ListNode {
* int val;
* ListNode next = null;
* public ListNode(int val) {
* this.val = val;
* }
* }
*/
//代码较为冗余,可以进行一定的优化
public class Solution {
/**
* 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
*
* @param head ListNode类
* @return ListNode类
*/
public ListNode oddEvenList (ListNode head) {
// write code here
if(head == null){
return null;
}
//奇偶链表头节点
ListNode dummyOdd = new ListNode(0);
ListNode dummyEven = new ListNode(0);
//双指针
ListNode left = head;
ListNode right = head.next;
//奇数节点链表和偶数节点链表辅助指针
ListNode indexOdd = left;
ListNode indexEven = right;
//连接奇数节点链表和偶数节点链表头节点
dummyOdd.next = indexOdd;
dummyEven.next = indexEven;
//遍历链表
while(right != null && right.next != null){
//移动双指针
left = left.next.next;
right = right.next.next;
//分别组成奇数节点链表和偶数节点链表
indexOdd.next = left;
indexEven.next = right;
indexOdd = left;
indexEven = right;
}
//合并奇数节点链表和偶数节点链表
left.next = dummyEven.next;
return dummyOdd.next;
}
}
时间复杂度:O(n),遍历一次链表的所有节点
空间复杂度:O(1),常数级指针,无额外辅助空间