简单的对java链表的相关实现做了个总结,如有考虑不周的,请指正。
1.创建链表
2.遍历
3.指定位置插入节点以及返回链表长度
4.倒数第k个节点
5.删除第index个节点
6.在不知道头指针的情况下删除指定节点
7.删除链表中的重复节点
8.选择排序,按照从小到大的顺序
9.插入排序,按照从小到大的顺序
10.从尾到头打印单链表(递归和非递归)
11.单链表反转(递归和非递归)
12.判断单链表是否有环、环的长度以及环的起始节点
13.合并两个有序链表(递归和非递归)
14.两个单链表相交的第一个节点
附代码:
package cw;
import java.util.Stack;
/**
* 链表
* @author cw
*/
public class Lianbiao {
static Node head = null;
//测试main方法
public static void main(String[] args) throws InterruptedException {
Node node = null;
//1.创建链表
for(int i=0;i<3;i++){
node = new Node(i);
addNode(node);
}
//2.遍历
System.err.println("=========遍历链表=========");
printNode(head);
Thread.sleep(2000);
//3.指定位置插入节点
node = new Node(100);
insertNodeByIndex(node,4);
//遍历
System.err.println("=========插入指定位置后,遍历链表=========");
printNode(head);
Thread.sleep(2000);
//4.倒数第k个节点
int k = 5;
Node inverseNode = getInverseNodeByIndex(head,k);
System.err.println("=========倒数第k个节点=========");
printNode(inverseNode);
Thread.sleep(2000);
//5.删除第index个节点
int index = 1;
Node deleteNodeByIndex = deleteNodeByIndex(head,index);
System.err.println("=========删除链表指定索引的节点=========");
printNode(deleteNodeByIndex);
Thread.sleep(2000);
//6.在不知道头指针的情况下删除指定节点(不是很完善)
Node test = head;
Node deleteNode = deleteNode(test);
System.err.println("=========在不知道头指针的情况下,删除链表指定节点=========");
printNode(deleteNode);
Thread.sleep(2000);
//7.删除链表中的重复节点
Node duplecate = new Node(1);
duplecate.next = new Node(1);
duplecate.next.next = new Node(2);
duplecate.next.next.next = new Node(3);
duplecate.next.next.next.next = new Node(2);
Node duplecatedeleteNode = getDuplecateDelete(duplecate);
System.err.println("=========删除链表中的重复节点后,遍历链表=========");
printNode(duplecatedeleteNode);
Thread.sleep(2000);
//8.选择排序,按照从小到大的顺序
Node select = new Node(4);
select.next = new Node(3);
select.next.next = new Node(2);
select.next.next.next = new Node(5);
select.next.next.next.next = new Node(1);
Node selectNode = selectSortNode(select);
System.err.println("=========选择排序后,遍历链表=========");
printNode(selectNode);
Thread.sleep(2000);
//9.插入排序,按照从小到大的顺序
Node insert = new Node(4);
insert.next = new Node(3);
insert.next.next = new Node(2);
insert.next.next.next = new Node(5);
insert.next.next.next.next = new Node(1);
Node insertNode = insertSortNode(insert);
System.err.println("=========插入排序后,遍历链表=========");
printNode(insertNode);
Thread.sleep(2000);
//10.从尾到头打印单链表
System.err.println("=========从尾到头打印单链表=========");
//递归
System.out.println("开始打印链表");
reversePrint(head);
//非递归
//reversePrint2(head);
System.out.println("结束打印链表");
Thread.sleep(2000);
//11.单链表反转
Node reverseNode = null;
//递归
reverseNode = reverseNode(head);
//非递归
//reverseNode = reverseNode2(head);
System.err.println("=========反转链表后,遍历链表=========");
printNode(reverseNode);
Thread.sleep(2000);
//12.判断单链表是否有环
head = null;
for(int i=0;i<3;i++){
node = new Node(i);
addNode(node);
}
addNode(head);//将头结点添加到链表当中,于是,单链表就有环了
System.err.println("=========判断单链表是否有环=========");
Node cycleNode = hasCycle(head);
if(cycleNode == null){
System.out.println("无环");
}else{
System.out.println("有环");
int cycleLength = getCycleLength(cycleNode);
System.out.println("环的长度为:"+cycleLength);
Node cycleStartNode = getCycleStart(head , cycleLength);
System.out.print("环的起始节点为:");
System.out.println(cycleStartNode.data);
}
Thread.sleep(2000);
//13.合并两个有序链表
Node l1 = new Node(10);
l1.next = new Node(20);
l1.next.next = new Node(50);
l1.next.next.next = new Node(70);
Node l2 = new Node(30);
l2.next = new Node(40);
l2.next.next = new Node(50);
Node mergeNode = null;
//递归
//result = mergeTwoLists(mergeNode,l1, l2);
//非递归
mergeNode = mergeTwoLists2(l1, l2);
System.err.println("=========合并链表后,遍历链表=========");
printNode(mergeNode);
Thread.sleep(2000);
//14.两个单链表相交的第一个节点
head = null;
for(int i=0;i<3;i++){
node = new Node(i);
addNode(node);
}
l1.next.next = head;//10,20,0,1,2
l2.next = head;//30,0,1,2
System.err.println("=========两个单链表相交的第一个节点=========");
Node firstCommonNode = getFirstCommonNode(l1,l2);
System.out.print("两个单链表相交的第一个节点为:");
System.out.println(firstCommonNode.data);
}
/**
* 1.创建链表
* @param node
*/
public static void addNode(Node node){
if (head == null) {
head = node;
return;
}
Node temp = head;
while(temp.next != null){
temp = temp.next;
}
temp.next = node;
}
/**
* 2.遍历链表
* @param head
*/
public static void printNode(Node head){
System.out.println("开始打印链表");
Node temp = head;
if(temp == null)
System.out.println("链表为空");
while(temp!=null){
System.out.println(temp.data);
temp = temp.next;
}
System.out.println("结束打印链表");
}
//返回链表长度
public static int getLength(Node head) {
int length = 0;
Node tmp = head;
while (tmp != null) {
length++;
tmp = tmp.next;
}
return length;
}
/**
* 3.插入指定位置节点
* @param node
* @param index
* @return
*/
public static Node insertNodeByIndex(Node node,int index){
if(index<1 || index>getLength(head)+1){
System.out.println("插入位置不合法。");
return null;
}
//链表为空
if(head == null){
head = node;
return head;
}
//在头部插入
if(index == 1){
node.next = head;
head = node;
return head;
}
Node pre = head;
Node cur = head.next;
//记录位置,要是2才行呀,因为cur起始已经指在第二个位置了
int i = 2;
while(cur != null){
//判断是否到达指定位置
if(index == i){
//在中间插入,插入节点的位置不在链表两头,在中间的某个位置
node.next = pre.next;
pre.next = node;
return head;
}
//前一个结点和当前结点后移
pre = cur;
cur = cur.next;
i++;
}
//在尾部插入
pre.next = node;
return head;
}
/**
* 4.倒数第k个节点
* @param head
* @param k
* @return
*/
private static Node getInverseNodeByIndex(Node head, int k) {
Node node = null;
if(head == null || k <= 0 || k > getLength(head)){
System.out.println("没有该节点,无法打印");
return node;
}
Node first = head;
Node second = head;
//第二个节点先走k个
for(int i=0; i<k; i++){
second = second.next;
}
//然后两个节点同时走,直到第二个节点为null时,first就是倒数第k个节点
while(second != null){
first = first.next;
second = second.next;
}
//只取倒数第k个节点
node = new Node(first.data);
return node;
}
/**
* 5.删除第index个节点
* @param head
* @param index
* @return
*/
private static Node deleteNodeByIndex(Node head, int index) {
if(index < 1 || index > getLength(head)){
System.out.println("删除位置不合法。");
return null;
}
if(index == 1){
head = head.next;
return head;
}
//前一个节点
Node pre = head;
//当前节点
Node cur = head.next;
int i = 1;
while(cur != null){
if(i == index){
//删除指定节点
pre.next = cur.next;
return head;
}else{
//前一个节点后移
pre = cur;
//当前节点后移
cur = cur.next;
i++;
}
}
return head;
}
/**
* 6.在不知道头指针的情况下删除指定节点
* @param test
* @return
*/
private static Node deleteNode(Node test) {
if(test == null || test.next == null){
System.out.println("删除不了啊");
return null;
}
//将要删除的节点跟他的后一个节点交换数据,删除它的后一个节点即可
int temp = test.data;
test.data = test.next.data;
test.next.data = temp;
//删除后一个节点
test.next = test.next.next;
return head;
}
/**
* 7.删除链表中的重复节点
* @param head
* @return
*/
//一个没有排序的单链表,请去掉重复项,并保留原顺序
private static Node getDuplecateDelete(Node head) {
if(head == null || head.next == null){
return head;
}
Node p = head;
//p节点从头开始,进行循环
while(p !=null){
//q节点从p节点开始,进行循环,分别与p节点比较,相等,q便删除
Node q = p;
while(q.next != null){
if(p.data == q.next.data){
q.next = q.next.next;
}else{
//q循环
q = q.next;
}
}
//p循环
p = p.next;
}
return head;
}
/**
* 8.选择排序,按照从小到大的顺序
* @param head
* @return
*/
//使用双层遍历。第一层遍历,正常遍历链表,第二层遍历,遍历第一层遍历时所用的结点后面所有结点并与之比较
private static Node selectSortNode(Node head) {
if(head == null || head.next == null){
System.out.println("无需排序");
return head;
}
//前一个节点
Node pre = head;//第一层遍历使用的移动指针,最初指向头结点
while(pre.next != null){//第一层遍历链表,从第一个结点开始遍历
//当前节点
Node cur = pre.next;//第二层遍历使用的移动指针,cur指向第二个结点开始
while(cur != null){//第二层遍历,从第二个结点开始遍历
第二层中的所有结点依次与第一次遍历中选定的结点进行比较,把最小的放到当前节点
if(pre.data > cur.data){
int temp = pre.data;
pre.data = cur.data;
cur.data = temp;
}
//循环当前节点,第二层遍历
cur = cur.next;
}
//循环前一个节点,第一层遍历
pre = pre.next;
}
return head;
}
/**
* 9.插入排序,按照从小到大的顺序
* @param head
* @return
*/
//分两组,一组当成有序序列,一组当成无序,将无序组中的元素与有序组中的元素进行比较,构建一个空的链表当成有序序列,而原先的旧链表为无序序列
private static Node insertSortNode(Node head) {
if(head == null || head.next == null){
System.out.println("无需排序");
return head;
}
//旧链表的移动指针
Node headTemp = head;
//新链表的头结点
Integer mix = Integer.MIN_VALUE;
Node newHead = new Node(mix);
//新链表的移动指针
Node newTemp = newHead;
//将第一个结点直接放入新链表中
if(newTemp.next == null){
Node node = new Node(headTemp.data);
newTemp.next = node;
headTemp = headTemp.next;//旧链表中指针移到下一位
}
//遍历现有链表
while(headTemp != null){
while(newTemp.next != null){
//先跟新链表中的第一个结点进行比较,注意是在第一个位置上增加结点
if(newTemp.next.data > headTemp.data){
Node node = new Node(headTemp.data);
node.next = newTemp.next;
newTemp.next = node;
break;
}
//如果不符合,则跟新链表中第二个结点进行比较
newTemp = newTemp.next;
}
//如果都不符合,跳出while,判断是否是到了新链表的最后一个结点,到达最末尾还没符合,那么说明该值是新链表中最大的数,如果是则直接在新链表后面添加即可
if(newTemp.next == null){
Node node = new Node(headTemp.data);
newTemp.next = node;
}
//旧链表指针指向下一位结点,继续重复和新链表中的结点进行比较
headTemp = headTemp.next;
//新链表中的移动指针需要复位,指向头结点
newTemp = newHead;
}
//返回新链表,旧链表等待垃圾回收机制将其收回,第一个mix节点舍弃
return newHead.next;
}
/**
* 10.从尾到头打印单链表
* @param head
*/
//使用系统的栈:递归;有个问题:当链表很长的时候,就会导致方法调用的层级很深,有可能造成栈溢出
private static void reversePrint(Node head) {
if(head == null)
return;
reversePrint(head.next);
System.out.println(head.data);
}
//对于这种颠倒顺序的问题,我们应该就会想到栈,后进先出
private static void reversePrint2(Node head) {
if(head == null)
return;
Stack<Node> stack = new Stack<Node>();
Node temp = head;
while(temp != null){
stack.push(temp);
temp = temp.next;
}
while(stack.size() > 0){
System.out.println(stack.pop().data);
}
}
/**
* 11.单链表反转
* @param head
* @return
*/
//递归反转单链表
private static Node reverseNode(Node head) {
if(head ==null || head.next ==null)
return head;
Node pre = head;//前一个节点
Node cur = head.next;//当前节点
Node temp = reverseNode(cur);// 先反转后续节点,即当前节点cur
cur.next = pre;//将当前结点的指针域指向前一结点
pre.next = null;// 前一结点的指针域令为null;
return temp;// 反转后新链表的头结点
}
//非递归反转单链表
private static Node reverseNode2(Node head) {
//如果链表为空或者只有一个节点,无需反转
if(head ==null || head.next ==null)
return head;
Node pre = head;//前一个节点
Node cur = head.next;//当前节点
Node next = null;//下一个节点
while(cur != null){//当前结点为null,说明位于尾结点,pre就是尾结点
next = cur.next;//用next保存cur之后的节点,因为cur.next要放前一个节点
cur.next = pre;//cur.next放前一个节点
// 指针往下移动
pre = cur;
cur = next;
}
// 最后将原链表的头节点的指针域置为null
head.next = null;
//新链表的头结点,即原链表的尾结点
return pre;
}
/**
* 12.单链表是否有环
* @param head
* @return
*/
public static Node hasCycle(Node head) {
if (head == null) {
return null;
}
Node first = head;
Node second = head;
while (second != null && second.next != null) {
first = first.next; //first指针走一步
second = second.next.next; //second指针走两步
if (first == second) { //一旦两个指针相遇,说明链表是有环的
return first;
}
}
return null;
}
//单链表中环的长度
private static int getCycleLength(Node cycleNode) {
int length = 0;
if(cycleNode == null)
return length;
Node temp = cycleNode;
while(temp != null){
length++;
temp = temp.next;
if(temp == cycleNode){//当temp结点走到原点cycleNode的时候
return length;
}
}
return length;
}
//环的起始节点
private static Node getCycleStart(Node head, int cycleLength) {
if (head == null) {
return null;
}
Node first = head;
Node second = head;
//先让second指针走length步
for (int i = 0; i < cycleLength; i++) {
second = second.next;
}
//然后让first指针和second指针同时各走一步
while (first != null && second != null) {
first = first.next;
second = second.next;
if (first == second) { //如果两个指针相遇了,说明这个结点就是环的起始点
return first;
}
}
return null;
}
/**
* 13.合并两个有序链表
* @param list1
* @param list2
* @return
*/
//非递归合并两个有序链表
public static Node mergeTwoLists2(Node list1,Node list2) {
if(list1 == null)
return list2;
if(list2 == null )
return list1;
Node tmp1 = list1;
Node tmp2 = list2;
int mix = Integer.MIN_VALUE;
Node temp = new Node(mix);
//这里不能把返回链表赋值为null,因为下一行马上就要把它赋值给另一链表,得让它在内存里有位置才行
Node headNew = temp;
while(tmp1 != null && tmp2!=null){
if(tmp1.data <= tmp2.data) {
temp.next=tmp1;
temp = temp.next;
tmp1 = tmp1.next;
} else{
temp.next=tmp2;
temp = temp.next;
tmp2=tmp2.next;
}
}
//其中一个链表已经跑到头之后,继续单链表的合并
while(tmp1 != null){
temp.next = tmp1;
temp = temp.next;
tmp1= tmp1.next;
}
while(tmp2 != null){
temp.next = tmp2;
temp = temp.next;
tmp2= tmp2.next;
}
return headNew.next;
}
//递归合并两个有序链表
private static Node mergeTwoLists(Node node,Node l1, Node l2) {
Node result = node;
if(l1 == null && l2 ==null)
return null;
if(l1 == null)
return l2;
if(l2 == null)
return l1;
if(l1.data > l2.data){
result = l2;
l2 = l2.next;
}else {
result = l1;
l1 = l1.next;
}
result.next = mergeTwoLists(result.next,l1,l2);
return result;
}
/**
* 14.两个单链表相交的第一个节点
* @param l1
* @param l2
* @return
*/
private static Node getFirstCommonNode(Node l1, Node l2) {
if(l1 == null || l2 == null)
return null;
int length1 = getLength(l1);
int length2 = getLength(l2);
//在较长的链表上走 |length1-length2| 步
//两个单链表如果相交的话,从相交起的第一个节点到之后的尾结点都是一样的(next不可能存两个地址),
//所以两个链表同时从同一个位置出发,找到的第一个相同的结点就是它们的第一个交点
int length = Math.abs(length1-length2);
if(length1 > length2){
for(int i=0;i<length;i++){
l1 = l1.next;
}
}else{
for(int i=0;i<length;i++){
l2 = l2.next;
}
}
//同时在两个链表上遍历,找到的第一个相同的结点就是它们的第一个交点
while(l1 != null && l2 !=null){
if(l1 == l2){
return l1;
}
l1 = l1.next;
l2 = l2.next;
}
return null;
}
}
class Node {
//用public,就可以直接调用data和next
public int data;
public Node next;
public Node(int data) {
this.data = data;
}
//用private就必须使用get/set了
/*private int data;
private Node next;
public Node(int data) {
this.data = data;
}
public int getData() {
return data;
}
public void setData(int data) {
this.data = data;
}
public Node getNext() {
return next;
}
public void setNext(Node next) {
this.next = next;
}*/
}