力扣初级算法链表(一)
一.反转链表
使用递归方法对不含头结点的单链表进行反转。
/**
* 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 reverseList(ListNode head) {
if(head == null || head.next == null){
return head; //如果单链表为空或只有一个元素,不用反转。
}
else{
return reverse(head,head.next,head);
}
}
public ListNode reverse(ListNode l1,ListNode l2,ListNode head){ //表示在以head为首结点的链表中,反转l1和l2这两个相邻的结点
if(l1 == head){
l1.next = null; //反转前首结点反转后的成为最后一个结点
}
if(l2.next == null){
l2.next = l1; //遍历至链表的最后,返回反转后的首结点
return l2;
}else{
ListNode p = l2.next;
l2.next = l1;
return reverse(l2,p,head); //递归
}
}
}
二.合并两个不含头结点的有序链表(这里的head指首结点)
/**
* 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 list1, ListNode list2) {
if((list1 == null)||(list2 == null)){
return (list1 == null)?list2:list1; //有一个链表为null,不用合并,直接返回另一个链表。
}else{
ListNode head = (list1.val>list2.val)?list2:list1; //先求出要返回的首结点
ListNode l = new ListNode();
while((list1!=null)&&(list2!=null)){ //当有一个链表插入完了,另一个链表剩余的部分自动摆在合并后链表的末尾
if(list2.val>=list1.val){ //根据两个链表中的数的大小,进行插入操作。
l.next = list1;
l = list1;
list1 = list1.next;
l.next = list2;
}else{
l.next = list2;
l = list2;
list2 = list2.next;
l.next = list1;
}
}
return head;
}
}
}
三.判断回文链表
当一个链表从前到后和从后到前的顺序一致时,该链表为回文链表。
如链表 1 1 2 1不是回文链表,而1 2 2 1是回文链表。
要求编写一个简单算法判断一个单链表是否为回文链表,要求时间复杂度为O(n),空间复杂度为O(1).
解题思路:
1.对要判断的链表的后一半元素进行(一)中的反转操作。
2.两指针分别从链表两头开始,向中间移动,比较两个指针的数据是否相同,如果两个指针移动到正中间仍然相同,则该链表为回文链表,否则不是回文链表。
/**
* 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 boolean isPalindrome(ListNode head) {
boolean result = true; //判断是否为回文链表
ListNode middle = head; //用于记录链表正中间的结点。
ListNode sequence = head; //用于记录链表首结点
int nodeCount = 0; //计算链表中元素数量
while(head!=null){
nodeCount++;
head = head.next; //获取了结点的个数
}
for(int i=0;i<nodeCount/2;i++){
middle = middle.next; //找到正中间的结点
}
ListNode reverse = reverseList(middle); //反转后一半链表
for(int i = 0;i<nodeCount/2;i++){
if(reverse.val == sequence.val){
reverse = reverse.next;
sequence = sequence.next;
}else{
result = false; //如果有一组不一样,就不是回文。
break;
}
}
return result;
}
/*
以下代码和(一)中利用递归操作反转链表代码相同
*/
public ListNode reverseList(ListNode head){
if((head == null)||(head.next == null)){
return head;
}else{
return reverse(head,head.next,head);
}
}
public ListNode reverse(ListNode l1,ListNode l2,ListNode head){
if(l1 == head){
l1.next = null;
}
if(l2.next == null){
l2.next = l1;
return l2;
}else{
ListNode p = l2.next;
l2.next = l1;
return reverse(l2,p,head);
}
}
}
四.给出某不含头结点的链表中某节点(不是尾结点),要求不访问该链表的首结点,删除该结点。
思路:这道题只需将后面的结点移动到前面来就可以了。但与平时我们删除结点的做法不同。平时我们需要知道要删除结点的头一个结点和后一个结点。
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode(int x) { val = x; }
* }
*/
class Solution {
public void deleteNode(ListNode node) {
while(true){
node.val = node.next.val; //将后一个值移动到前一个值的位置。
if(node.next.next == null){
node.next = null; //最后一个结点的next直接赋为null。
break;
}
node = node.next;
}
}
}
注意这里题目中说的不是尾结点的含义,如果删除的是尾结点,那么就必须要知道要删除结点的前一个结点了。这种写法:node.next.next当node为最后一个结点时,编译也会抛出空指针异常。
五.删除某不含头结点的单链表中倒数第N个结点
/**
* 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 removeNthFromEnd(ListNode head, int n) {
int nodeNum = 1; //记录单链表结点的个数
ListNode p = head;
while(true){
if(p.next!=null){
nodeNum++; //计算单链表结点的个数
p = p.next;
}else{
break;
}
}
p = head; //删除前的首结点
if(nodeNum == n){ //如果要删除倒数第N个结点,就是删除第一个结点,直接返回链首指针的下一个结点。
return p.next;
}else{
for(int i=0;i<nodeNum-n-1;i++){
p = p.next; //否则找到要删除结点的前一个位置。
}
p.next = p.next.next; //删除该结点。
return head; //返回首结点。
}
}
}