1.链表的实体类:
public class ListNode {
int val;
ListNode next = null;
ListNode(int val) {
this.val = val;
}
}
2.链表的部分操作类:
public class SolutListNode {
ListNode head = null;
public ListNode FindKthToTail(ListNode head, int k) {
// 一种思路是先遍历一遍求长度,然后输出倒数k个
// 正常的思路是,设置两个游标,让快的领先k个
ListNode slow = head;
ListNode fast = head;
if (head == null || k <= 0) {
return null;
}
for (int i = 1; i < k; i++) { // 快的先走k-1步,倒数第三个,其实应该快的指到第三个,只需要走两步即可。
if (fast.next == null) // 这个是k与链表长度的关系,如果,链表长度小于k,肯定在走到k之前就出现null,直接返回null即可
return null;
else
fast = fast.next;
}
while (fast.next != null) { // 快的从第k个,慢的从第1个,同时开始走。
slow = slow.next;
fast = fast.next;
}
return slow;
}
/**
* 链表添加结点: 找到链表的末尾结点,把新添加的数据作为末尾结点的后续结点
*
* @param data
*/
public void addNode(int data) {
ListNode newNode = new ListNode(data);
if (head == null) {
head = newNode;
return;
}
ListNode temp = head;
while (temp.next != null) {
temp = temp.next;
}
temp.next = newNode;
}
public void printLink(ListNode head) {
ListNode curNode = head;
while (curNode != null) {
System.out.print(curNode.val + " ");
curNode = curNode.next;
}
System.out.println();
}
//反转链表
public ListNode ReverseList1(ListNode head) {
if (head == null)
return null; // head为当前节点,如果当前节点为空的话,那就什么也不做,直接返回null;
ListNode pre = null;
ListNode next = null;
// 当前节点是head,pre为当前节点的前一节点,next为当前节点的下一节点
// 需要pre和next的目的是让当前节点从pre->head->next1->next2变成pre<-head next1->next2
// 即pre让节点可以反转所指方向,但反转之后如果不用next节点保存next1节点的话,此单链表就此断开了
// 所以需要用到pre和next两个节点
// 1->2->3->4->5
// 1<-2<-3 4->5
while(head != null){ //注意这个地方的写法,如果写head.next将会丢失最后一个节点
// 做循环,如果当前节点不为空的话,始终执行此循环,此循环的目的就是让当前节点从指向next到指向pre
// 如此就可以做到反转链表的效果
// 先用next保存head的下一个节点的信息,保证单链表不会因为失去head节点的原next节点而就此断裂
next = head.next; //先让head.next指向的节点,即第二个节点叫next
head.next = pre; //将head.next指向pre,也就是说断开head节点与后面的连接
pre = head;//pre,head依次向后移动一个节点,进行下一次反转
head = next;
}
// 如果head为null的时候,pre就为最后一个节点了,但是链表已经反转完毕,pre就是反转后链表的第一个节点
return pre;
}
//合并两个递增的链表并且保证最终的链表也是单调不减的。
public ListNode Merge(ListNode list1,ListNode list2) {
if (list1 == null) {
return list2;
}
if (list2 == null) {
return list1;
}
//新建一个用于存放融合之后的链表
//因为,融合的过程中链表是一直移动的,所以要把链表的头保存下来,才能返回正确的一条链
ListNode res = new ListNode(0);
ListNode merlistNode = res;
while (list1 != null && list2 != null) { //依次比较,将较小的节点连到融合节点上
if(list1.val < list2.val){
merlistNode.next = list1; //连上小的list1
list1 = list1.next; //list1 可以往后移动一个,下次用移动后的和list2比较
merlistNode = merlistNode.next;// merlistNode也往后移动一个
}
else {
merlistNode.next = list2;
list2 = list2.next;
merlistNode = merlistNode.next;
}
}
//把未结束的链表连接到合并后的链表尾部
if(list1 != null)
merlistNode.next = list1;
if(list2 != null)
merlistNode.next = list2;
return res.next;
}
//递归的方式融合
public ListNode Merge2(ListNode list1,ListNode list2) {
if (list1 == null) {
return list2;
}
if (list2 == null) {
return list1;
}
ListNode newHead = null;
if (list1.val <= list2.val) {
newHead = list1;
newHead.next = Merge(list1.next,list2);
}else {
newHead = list2;
newHead.next = Merge(list1,list2.next);
}
return newHead;
}
}
反转的原理分析:
过程就是:head的下一个名为next
断开head与next的连接,转为指向前面的pre。
pre、head依次往前移动
知道head==null结束,返回pre
3、测试类:
/**
* 三个类:ListNode SolutListNode 和 本类是测试的
* @author Administrator
*
*/
public class TestListNode {
public static void main(String[] args) {
SolutListNode myliListNode = new SolutListNode();//ListNode 只是其中的一个节点,而myliListNode.head就是两个链表的头,找到头就依次可以了
myliListNode.addNode(1);
myliListNode.addNode(2);
myliListNode.addNode(3);
myliListNode.addNode(5);
myliListNode.addNode(8);
myliListNode.printLink(myliListNode.head);
//输出反转之后的链表
//ListNode reverListNode = myliListNode.ReverseList1(myliListNode.head);
//myliListNode.printLink(reverListNode);
//输出倒数第k个节点
//ListNode nk = myliListNode.FindKthToTail(myliListNode.head, 2);
//System.out.println(nk.val);
//输出融合排序后的链表
SolutListNode mlist2 = new SolutListNode();
mlist2.addNode(4);
mlist2.addNode(6);
mlist2.addNode(9);
ListNode resListNode = mlist2.Merge(myliListNode.head, mlist2.head);
mlist2.printLink(resListNode);
}
}