OJ地址
现有一链表的头指针 ListNode* pHead,给一定值x,编写一段代码将所有小于x的结点排在其余结点之前,且不能改变原来的数据顺序,返回重新排列后的链表的头指针。
public ListNode partition(ListNode pHead, int x) {
// write code here
//存放小于x的区域
ListNode aHead = null;
ListNode aEnd = null;
//存放大于x的区域
ListNode bHead = null;
ListNode bEnd = null;
//用cur遍历,分开数据
ListNode cur = pHead;
while (cur != null) {
if (cur.val < x) {
//判断是否存在数据
if (aHead == null) {
aHead = cur;
aEnd = cur;
} else {
aEnd.next = cur;
aEnd = aEnd.next;
}
} else {
if (bHead == null) {
bHead = cur;
bEnd = cur;
} else {
bEnd.next = cur;
bEnd = bEnd.next;
}
}
cur = cur.next;
}
//情况讨论
//全部都是小于x的数据
if (aHead == null) {
return bHead;
}
aEnd.next = bHead;
//全部都是大于x的数据
if (bHead != null) {
//两种都有
bEnd.next = null;//注意。尾节点的结点域要置空
}
return aHead;
}
OJ地址
给你一个链表的头节点 head ,判断链表中是否有环。
思路:快慢指针,链表有环就是把尾节点的指针域指向任意一个结点,这样就创建一个带环链表,解决这种问题的思路还是快慢指针,快指针的速度一直是慢指针的2倍,打个比方,你和你朋友在操场跑步,只要一直跑下去,你们始终会相遇。
public boolean hasCycle(ListNode head) {
ListNode fast = head;
ListNode slow = head;
while (fast != null && fast.next != null) {
fast = fast.next.next;
slow = slow.next;
if (fast == slow) {
return true;
}
}
return false;
}
问题延申:如何找到环的长度呢?
答案:快慢指针相遇后继续移动,直到第二次相遇。两次相遇间的移动次数即为环的长度
OJ地址
给定一个链表的头节点 head ,返回链表开始入环的第一个节点。 如果链表无环,则返回 null。
思路1 :快慢指针
public ListNode detectCycle(ListNode head) {
ListNode fast = head;
ListNode slow = head;
while(fast != null && fast.next != null) {
fast = fast.next.next;
slow = slow.next;
if(fast == slow) {
fast = head;
while(fast != slow){
fast = fast.next;
slow = slow.next;
}
return fast;
}
}
return null;
}
思路2:利用哈希表不包含重复元素的特性。
public ListNode detectCycle(ListNode head) {
HashSet<ListNode> set = new HashSet<>();
while (head != null) {
if (set.contains(head)) {
return head;
}
set.add(head);
head = head.next;
}
return null;
}
OJ链接
将一个节点数为 size 链表 m 位置到 n 位置之间的区间反转,要求时间复杂度 O(n),空间复杂度 O(1)。
与之前的反转链表的思路是差不多的,只不过这里我们,需要再区间内反转,那么我们必须要找到第m个结点的前一个结点pre,接下来的思路如下图:
public ListNode reverseBetween (ListNode head, int m, int n) {
ListNode dummyHead = new ListNode(-1);
dummyHead.next = head;
ListNode pre = dummyHead;
n = n - m;
//找到m位置的前一个结点
while (m != 1) {
pre = pre.next;
m--;
}
ListNode cur = pre.next;
while (n != 0){
ListNode curNext = cur.next;
cur.next = curNext.next;
curNext.next = pre.next;
pre.next = curNext;
n--;
}
return dummyHead.next;
}
OJ链接
将给出的链表中的节点每 k 个一组翻转,返回翻转后的链表
如果链表中的节点数不是 k 的倍数,将最后剩下的节点保持原样
你不能更改节点中的值,只能更改节点本身。
思路:先获取链表长度,然后分为 size/k组,每一组依次反转,是上一题的思路的进阶
public ListNode reverseKGroup (ListNode head, int k) {
// write code here
ListNode dummyHead = new ListNode(-1);
dummyHead.next = head;
ListNode pre = dummyHead;
ListNode cur = head;
//获取长度
int size = 0;
while (cur != null) {
cur = cur.next;
size++;
}
//分为size / k 组
cur = head;
for (int i = 0; i < size / k; i++) {
for (int j = 0; j < k - 1; j++) {
ListNode curNext = cur.next;
cur.next = curNext.next;
curNext.next = pre.next;
pre.next = curNext;
}
pre = cur;
cur = cur.next;
}
return dummyHead.next;
}
OJ链接
给定一个单链表,请设定一个函数,将链表的奇数位节点和偶数位节点分别放在一起,重排后输出。
注意是节点的编号而非节点的数值。
思路:定义两个指针一个找奇结点,另一个找偶结点,然后分别链接,最后把奇结点的尾链接到偶结点的头。
public ListNode oddEvenList (ListNode head) {
if (head == null || head.next == null) {
return head;
}
//定义一个指向偶数结点的头指针
ListNode evenHead = head.next;
//一直指向偶结点
ListNode even = evenHead;
//一直指向奇结点
ListNode odd = head;
while (even != null && even.next != null) {
odd.next = even.next;
odd = odd.next;
even.next = odd.next;
even = even.next;
}
odd.next = evenHead;
return head;
}
OJ链接
假设链表中每一个节点的值都在 0 - 9 之间,那么链表整体就可以代表一个整数。
给定两个这种链表,请生成代表两个整数相加值的结果链表。
思路:由于加法是从最后开始计算的,所以我们先逆置链表,然后计算每一个结点的值,注意进位,然后逆置头节点。
public ListNode addInList (ListNode head1, ListNode head2) {
// write code here
if (head1 == null) {
return head2;
}
if (head2 == null) {
return head1;
}
//反转链表
head1 = reverse(head1);
head2 = reverse(head2);
ListNode head = new ListNode(0);
ListNode cur = head;
int carry = 0;
while (head2 != null || head1 != null) {
int sum = carry;
if (head1 != null) {
sum += head1.val;
head1 = head1.next;
}
if (head2 != null) {
sum += head2.val;
head2 = head2.next;
}
carry = sum / 10;
cur.next = new ListNode(sum % 10);
cur = cur.next;
}
if (carry != 0) {
cur.next = new ListNode(carry);
}
return reverse(head.next);
}
public ListNode reverse(ListNode head) {
ListNode pre = null;
ListNode cur = head;
while (cur != null) {
ListNode curNext = cur.next;
cur.next = pre;
pre = cur;
cur = curNext;
}
return pre;
}
思路:为了把数据逆置,我们可以利用栈的特性,把数据放入栈中,拿出来时就是从末尾开始,然后依次相加,注意进位。
public ListNode addTwoNumbers(ListNode l1, ListNode l2) {
Stack<Integer> stack1 = new Stack<>();
Stack<Integer> stack2 = new Stack<>();
while (l1 != null) {
stack1.push(l1.val);
l1 = l1.next;
}
while (l2 != null) {
stack2.push(l2.val);
l2 = l2.next;
}
int carry = 0;
ListNode head = null;
while (!stack2.empty() || !stack1.empty() || carry > 0) {
int sum = carry;
sum += stack1.empty() ? 0 : stack1.pop();
sum += stack2.empty() ? 0 : stack2.pop();
ListNode node = new ListNode(sum % 10);
carry = sum / 10;
//头插
node.next = head;
head = node;
}
return head;
}
OJ链接
class Solution {
public void reorderList(ListNode head) {
if (head == null || head.next == null) {
return;
}
//找到中间结点
ListNode mid = findMid(head);
ListNode front = head;
ListNode back = mid.next;
mid.next = null;
//反转后面的结点
back = reverse(back);
//依次插入前半部分间隙
merge(front, back);
}
public ListNode findMid(ListNode head) {
ListNode fast = head;
ListNode slow = head;
while (fast != null && fast.next != null) {
fast = fast.next.next;
slow = slow.next;
}
return slow;
}
public ListNode reverse(ListNode head) {
ListNode pre = null;
ListNode cur = head;
while (cur != null) {
ListNode curNext = cur.next;
cur.next = pre;
pre = cur;
cur = curNext;
}
return pre;
}
public void merge(ListNode front, ListNode back) {
while (front != null && back != null) {
ListNode frontNext = front.next;
ListNode backNext = back.next;
front.next = back;
front = frontNext;
back.next = front;
back = backNext;
}
}
}