java链表的相关实现

简单的对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;
	}*/
	
}


  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值