LinkedList 链表

单向链表节点定义

只包含item,和next指针:


单项链表的基本操作

0. 获取链表长度:

	public static <T> int length(Node<T> head) {
		int length = 0;
		while (head != null) {
			length++;
			head = head.next;
		}
		return length;
	}

1. 在链表头插入节点:

	/**
	 * 在链表首插入节点。将新节点的next指向head,然后让新节点作为head。
	 * head->Node0
	 * Node->head->Node0
	 */
	public static<T> Node<T> insertHead(Node<T> head, T item) {
		Node<T> node = new Node<T>(item);
		node.next = head;
		head = node;
		return head;
	}

2. 在链表尾插入节点:

	/**
	 * 在链表尾插入节点。链表尾的next指向新节点即可。
	 * head->null
	 * head->Node->null
	 */
	public static<T> void insertTail(Node<T> head, T item) {
		while(head.next != null) {
			head = head.next;
		}
		head.next = new Node<T>(item);
	}

3. 在链表中间插入节点:


	/**
	 * 在链表中间插入新节点、在两节点中间插入一个新节点,要知道这两个前后节点的指针。
	 * start->end
	 * start->mid->end;
	 *   0 ->  1  -> 2 
	 */
	public static<T> Node<T> insertMid(Node<T> head, int position, T item) {
		if (position == 0) {
			return insertHead(head, item);
		}
		Node<T> current = head;
		// 得到start节点的指针
		while(position > 1) {
			current = current.next;
			position--;
		}
		Node<T> node = new Node<T>(item);
		// 得到end节点的指针
		Node<T> temp = current.next;
		current.next = node;
		node.next = temp;
		return head;
	}

4. 删除链表头节点:

	/**
	 * 删除链表首。将head指向head的next即可。
	 * head->Node->Node->null
	 */
	public static<T> Node<T> deleteHead(Node<T> head) {
		head = head.next;
		return head;
	}

5. 删除链表尾节点:

	/**
	 * 删除链表尾。将倒数第二个的next置为null即可。
	 * head->Node->Node->null
	 */
	public static<T> Node<T> deleteTail(Node<T> head) {
		if (head.next == null) {
			return null;
		}
		Node<T> runner = head;
		while(runner.next.next != null) {
			runner = runner.next;
		}
		runner.next = null;
		return head;
	}

6. 删除链表中间节点:

如果不删除尾节点,使用偷龙换凤:


	/**
	 * 删除链表的中间节点。实际上删除待删节点的下一个节点,将待删节点的下一个节点的数据覆盖到待删节点。不能删除尾节点。
	 * A->Node->B->C
	 */
	public static<T> void deleteMid(Node<T> node) {
		Node<T> temp = node.next.next;
		node.item = node.next.item;
		node.next = temp;
	}

一般解法:


       /**
	 * 删除链表的中间元素。要知道待删节点的前后节点。将前节点的next指向后节点即可。。
	 * start->Node->end
	 * 0 ->  1 -> 2
	 */
	public static<T> void deleteMid(Node<T> head, int position) {
		while(position > 1) {
			head = head.next;
			position--;
		}
		head.next = head.next.next;
	}

7. 找到链表的第k个节点:

      /**
	 * 找到链表中的第k个节点。依次遍历
	 * head->Node->Node->Node->Node->null;
	 *   0 ->  1 ->  2  -> 3  -> 4 -> null
	 */
	public static<T> Node<T> findKth(Node<T> head, int k) {
	        for (int i = 0; i < k; i++) {
		        head = head.next;
	        }
		return head;
	}

8. 找到链表的中间节点:

奇数个节点:


偶数个节点:


	/**
	 * 找中间节点。快慢指针法,慢指针走一步,快指针走两步。
	 * 当有偶数个节点时,慢指针走到中间时,快指针指向链表尾:fast.next == null
	 * head->slow->Node->fast->null;
	 * 当有奇数个节点时,慢指针走到中间时,快指针指向null:fast == null
	 * head->Node->slow->Node->Node->null;
	 */
	public static<T> Node<T> findMid(Node<T> head) {
		Node<T> slow = head;
		Node<T> fast = head.next;
		while(fast != null && fast.next != null) {
			slow = slow.next;
			fast = fast.next.next;
		}
		return slow;
	}

9. 找到链表倒数第k个节点:


	/**
	 * 找到链表中的倒数第k个节点。双指针法。
	 * 快指针为null时,慢指针指向要找的节点。
	 * head->Node->slow->Node->Node->null;
	 *	 4 ->  3  -> 2  -> 1 -> 0
	 */
	public static<T> Node<T> findLastKth(Node<T> head, int k) {
		Node<T> slow = head;
		Node<T> fast = head.next;
		for (int i = 0; i <= k; i++) {
			fast = fast.next;
		}
		while(fast != null) {
			fast = fast.next;
			slow = slow.next;
		}
		return slow;
	}

10. 翻转链表:


/**
	 * 翻转链表。每次翻转一个节点的next指针,要知道前后两个节点。将该节点的next指向之前的节点,runner指向之后的节点。
	 * 翻转一个指针,断开下一个指针。
	 * 
	 * A->B->C->D->null
	 * null<-A B->C->D->null
	 * null<-A<-B C->D->null
	 * null<-A<-B<-C D->null
	 * null<-A<-B<-C<-D 
	 */
	public static<T> Node<T> reverse(Node<T> head) {
		Node<T> previous = null;
		Node<T> runner = head;
		// 四步交换
		while(runner != null) {
			Node<T> temp = runner.next;
			runner.next = previous;
			previous = runner;
			runner = temp;
		}
		return previous;
	}

11. 交换两个相邻节点:


	/**
	 * 交换链表中两个相邻节点。要知道前一个节点的parent指针。
	 * parentI->I->J->Node
	 * 先将I节点的next指向J节点的next:
	 * parentI->I->Node
	 * 	    J->Node
	 * 然后将J节点的next指向I节点:
	 * parentI->I->Node
	 *          J->I
	 * 最后将I父节点的next指向J节点 :
	 * parentI->J->I->Node
	 * 如果I节点是head时,新建一个dummy节点作为head的父节点。
	 */
	public static <T> Node<T> swapAdjacent(Node<T> head, int i) {
		Node<T> parentI;
		Node<T> nodeI;
		Node<T> nodeJ;
		if (i == 0) {
			if (head == null) {
				return head;
			}
			parentI = new Node<T>(null);
			parentI.next = head;
		} else {
			parentI = findKth(head, i - 1);
			if (parentI == null || parentI.next == null
					|| parentI.next.next == null) {
				return head;
			}
		}
		nodeI = parentI.next;
		nodeJ = nodeI.next;

		nodeI.next = nodeJ.next;
		nodeJ.next = nodeI;
		parentI.next = nodeJ;
		return i == 0 ? parentI.next : head;
	}

12. 交换任意两个节点:

	/**
	 * 交换链表中的任意两个节点。现在考虑两节点不相邻情况:
	 * parentI->I->Node1->parentJ->J->Node2
	 * 首先将I节点的next指向J节点的next:
	 * parentI->I->Node2  Node1->parentJ->J->Node2
	 * 然后将J节点的next指向原来I节点的next:
	 * parentI->I->Node2  Node1->parentJ->J->Node1
	 * 然后I节点的父节点指向J节点:
	 * parentI->J->Node1->parentJ->J  I->Node2
	 * 最后J节点的父节点指向I节点:
	 * parentI->J->Node1->parentJ->I->Node2
	 */
	public static <T> Node<T> swap(Node<T> head, int i, int j) {
		if (i == j) {
			return head;
		}
		if (i > j) {
			int temp = i;
			i = j;
			j = temp;
		}
		Node<T> parentI;
		if (i == 0) {
			parentI = new Node<T>(null);
			parentI.next = head;
		} else {
			parentI = findKth(head, i - 1);
			if (parentI == null || parentI.next == null) {
				return head;
			}
		}

		Node<T> parentJ = findKth(head, j - 1);
		if (parentJ == null || parentJ.next == null) {
			return head;
		}

		Node<T> nodeI = parentI.next;
		Node<T> nodeJ = parentJ.next;
		Node<T> nextI = nodeI.next;
		if (nextI == nodeJ) {
			nodeI.next = nodeJ.next;
			nodeJ.next = nodeI;
			parentI.next = nodeJ;
		} else {
			nodeI.next = nodeJ.next;
			nodeJ.next = nextI;
			parentI.next = nodeJ;
			parentJ.next = nodeI;
		}

		return i == 0 ? parentI.next : head;
	}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值