链表反转:
链表反转也是比较常见的一个面试题。
方法1. 迭代。 分别设置三个指针。第一个指针保持结果,第二个指针指向当前的节点,第三个指针保存下一个节点。
static void reverse(struct node** head_ref)
{
struct node* prev = NULL;
struct node* current = *head_ref;
struct node* next;
while (current != NULL)
{
next = current->next;
current->next = prev;
prev = current;
current = next;
}
*head_ref = prev;
}
方法2:使用递归
/* 使用递归的方法 */
static struct node * reverseRecall(struct node* head){
//最后一个节点会返回 作为头部
if(NULL == head || head->next == NULL) return head;
//head->next 表示剩下的部分
struct node * newHead = reverseRecall(head->next);
head->next->next = head; //颠倒指针
head->next = NULL;//原来的头节点 next 应该为空
return newHead;
}
链表排序:
对链表进行排序,要求的时间复杂度为O(n log n)。nlogn的排序有快速排序、归并排序、堆排序。双向链表用快排比较适合,堆排序也可以用于链表,单向链表适合用归并排序。题目要求的是常数的空间复杂度,因为这里用了递归,如果算上栈空间的话,也要 o(logn)的复杂度。
关于链表的划分,这里使用了快慢指针,从中间节点进行切开成单独的链表。在merge之后又会合并成一个链表
public static ListNode sortList(ListNode head) {
if(head == null || head.next == null) return head;
ListNode slow = head;
ListNode fast = head;
//用快慢指针找到中间节点
while(fast.next != null && fast.next.next != null){
slow = slow.next;
fast = fast.next.next;
}
ListNode list2 = slow.next;
slow.next = null;
head = sortList(head);
list2 = sortList(list2);
return merge(head, list2);
}
private static ListNode merge(ListNode list1, ListNode list2) {
if(list1 == null) return list2;
if(list2 == null) return list1;
ListNode newHead = new ListNode(0);//链表头不存储实际数据
ListNode last = newHead;
last = newHead;
//连接每个节点,只更换指针,因此空间复杂度为O(1)
while(list1 != null && list2 != null){
if(list1.val < list2.val){
last.next = list1;
list1 = list1.next;
}else{
last.next = list2;
list2 = list2.next;
}
last = last.next;
}
//最后剩余的部分,直接连接起来即可
if(list1 != null) last.next = list1;
else if(list2 != null) last.next = list2;
return newHead.next;
}
链表中倒数第K个节点:
第一个指针先走k-1步,然后第二个指针在head,两个指针同时走,当第一个指针走到最后时,第二个指针指向的就是倒数第K个节点。