JAVA数据结构-单链表 算法题
21. 合并两个有序链表
题目:将两个升序链表合并为一个新的 升序 链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。
输入:1->2->4, 1->3->4
输出:1->1->2->3->4->4
方法:
- 循环比较两链表的首个数据节点,将较小的值插入新链表.
- 类似于插入排序,以一个链表L1作为基础,将另外一个链表L2的节点不断遍历比较并插入L1.
- 将两链表相连,直接进行排序操作 (超时)
方法一思路:
- 创建一个只有头节点的新链表L3,创建临时指针t1 t2和t3分别指向L1和L2和L3;
- 循环比较t1和t2的值,将较小的一个节点连接在L3的末尾,使T3和较小值链表指针(t1或t2)后移一位;
- 当t1或t2后移后节点为空时,循环结束,将指针位空的队列相应的标识符flag置为true;
- 将遍历结束以后多的哪一个队列的剩余元素添加到L3尾部;
- 返回L3.next
代码:
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode() {}
* ListNode(int val) { this.val = val; }
* ListNode(int val, ListNode next) { this.val = val; this.next = next; }
* }
*/
class Solution {
public ListNode mergeTwoLists(ListNode l1, ListNode l2) {
if(l1 == null){
return l2;
}
if(l2 == null){
return l1;
}
ListNode head = new ListNode(0,null);
ListNode tempHead = head;
boolean flag1 = false;
boolean flag2 = false;
while(true){
if(l1.val >= l2.val){
tempHead.next = l2;
l2 = l2.next;
tempHead = tempHead.next;
if(l2 == null){
flag2 = true;
break;
}
}else{
tempHead.next = l1;
l1 = l1.next;
tempHead = tempHead.next;
if(l1 == null){
flag1 = true;
break;
}
}
}
if(flag1){
tempHead.next = l2;
}
if(flag2){
tempHead.next = l1;
}
return head.next;
}
}
使用递归的方法实现:
递归能简化代码,但是栈的开销比较大,并且递归层次太深会报栈溢出异常.
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode() {}
* ListNode(int val) { this.val = val; }
* ListNode(int val, ListNode next) { this.val = val; this.next = next; }
* }
*/
public ListNode mergeTwoLists(ListNode l1, ListNode l2) {
if (l1 == null) return l2;
if (l2 == null) return l1;
ListNode res = l1.val < l2.val ? l1 : l2;
res.next = mergeTwoLists(res.next, l1.val >= l2.val ? l1 : l2);
return res;
}
剑指 Offer 22. 链表中倒数第k个节点
题目:输入一个链表,输出该链表中倒数第k个节点。为了符合大多数人的习惯,本题从1开始计数,即链表的尾节点是倒数第1个节点。例如,一个链表有6个节点,从头节点开始,它们的值依次是1、2、3、4、5、6。这个链表的倒数第3个节点是值为4的节点。
给定一个链表: 1->2->3->4->5, 和 k = 2.
返回链表 4->5.
方法:
- 根据链表总共长度和k计算出需要返回节点的起始位置
- 使用快慢指针思想实现
方法一思路:
- 先把链表从头到尾遍历,得到链表有效节点的总长度lenth
- 使用length减去k得到开始遍历的倒数数的起始位置. start= length - k
- 需要判断length=0,传入链表为空
- start=0, k等于链表总长度,直接返回原链表
- start<0,k值大于链表总长度,返回null
- start>0,正常情况,从start开始遍历
代码:
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode(int x) { val = x; }
* }
*/
class Solution {
public ListNode getKthFromEnd(ListNode head, int k) {
//length =0
if(head == null){
return null;
}
//计算length
ListNode temp = head;
int length = 0;
while(true){
if(temp == null) break;
temp = temp.next;
length++;
}
//计算开始位置
int start = length - k;
if(start <0) {
return null;
}else if(start ==0){
return head;
}
//定位到start位置
temp = head;
while((start--) != 0){
temp = temp.next;
}
return temp;
}
}
方法二思路:
- 设置两个快慢指针front和behind,分别指向头数据节点
- 先让快指针走k步
- 然后两个指针同时走,当快指针走到头时,慢指针就是链表的倒数第k个节点.
代码:
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode(int x) { val = x; }
* }
*/
class Solution {
public ListNode getKthFromEnd(ListNode head, int k) {
ListNode frontNode = head, behindNode = head;
while (frontNode != null && k > 0) {
frontNode = frontNode.next;
k--;
}
while (frontNode != null) {
frontNode = frontNode.next;
behindNode = behindNode.next;
}
return behindNode;
}
}
剑指 Offer 24. 反转链表
题目:定义一个函数,输入一个链表的头节点,反转该链表并输出反转后链表的头节点。
输入: 1->2->3->4->5->NULL
输出: 5->4->3->2->1->NULL
方法:
- 头插法
- 借助栈
方法一思路:
- 先定义一个反转后链表的头节点reverseHead
- 遍历原来的链表head,每遍历一个节点,就将其取出,并放在新的链表reverseHead的最前端
- 返回reverseHead.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 || head.next == null){
return head;
}
ListNode reverseHead = new ListNode(0);
ListNode count = head;
while(true){
if(count == null) break;
//将count指向的数据节点拿出
ListNode temp = count;
count =count.next;
//插入到reverseHead的第一个数据元素
temp.next = reverseHead.next;
reverseHead.next = temp;
}
return reverseHead.next;
}
}
剑指 Offer 06. 从尾到头打印链表
题目:输入一个链表的头节点,从尾到头反过来返回每个节点的值(用数组返回)。
输入:head = [1,3,2]
输出:[2,3,1]
方法:
- 反转链表再打印,这样做的问题是回破坏原来的单链表结构,不建议
- 利用栈的特性,实现逆序打印
- 根据数组下标,反向插入数据,实现逆序
方法二思路:
- 遍历链表中的每一个节点,将节点的数据压入栈中,并计数count
- 创建count大小的数据,遍历数据,将pop出的结果放入指定下标位置.
代码:
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode(int x) { val = x; }
* }
*/
class Solution {
public int[] reversePrint(ListNode head) {
Stack<Integer> stack = new Stack();
ListNode temp = head;
int count=0;
while(temp != null){
stack.push(temp.val);
temp = temp.next;
count++;
}
int [] result = new int [count];
for(int i=0;i<count;i++){
result[i] = stack.pop();
}
return result;
}
}
方法三思路:
- 遍历链表统计出节点数count,创建长度为cont的数组
- 逆序遍历数组插入数据
代码:
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode(int x) { val = x; }
* }
*/
class Solution {
public int[] reversePrint(ListNode head) {
ListNode node = head;
int count = 0;
while (node != null) {
++count;
node = node.next;
}
int[] nums = new int[count];
node = head;
for (int i = count - 1; i >= 0; --i) {
nums[i] = node.val;
node = node.next;
}
return nums;
}
}