1. 程式人生 > >【題6 從尾到頭列印連結串列】

【題6 從尾到頭列印連結串列】

【題6 從尾到頭列印連結串列】
【題目】
輸入一個連結串列的頭節點,從頭到尾反過來打印出每個結點的值。
先問:
是否允許修改輸入的資料?
解決方案一:棧
首先遍歷連結串列的節點後列印,典型的“後進先出”,可以使用棧來實現這種順序。

  • 遍歷時候,每個結點放入棧中。
  • 遍歷完整連結串列後,從棧頂開始逐個輸出結點的值

解決方案二:遞迴
棧的本質就是遞迴,直接使用遞迴的方式,列印一個節點的時候先列印它後面的節點,再列印該節點自身,實現反向列印
解決方案三:ArratList
遍歷連結串列,把連結串列中的元素複製到ArrayList中,然後逆序列印ArrayList中的元素,由於ArrayList底層使用陣列實現,所以用陣列也是同樣的原理
解決方案四:指標反轉


前三種解決方案本身屬於在列印連結串列的時候不修改連結串列本身結構,在允許修改連結串列結構的情況下可以把連結串列中的節點指標反轉過來,改變連結串列方向,然後重新遍歷列印改變方向後的連結串列。

實現

package ti5to2;

import java.util.ArrayList;
import java.util.Stack;

class ListNode{
	int val;
	ListNode next = null;
	
	public ListNode(){
	}
	public ListNode(int value){
		this.val = value;
	}
}
public class No5PrintListFromTailToHead {
	public static void main (String[] args){
		ListNode node1 = new ListNode(1);
		ListNode node2 = new ListNode(2);
		ListNode node3 = new ListNode(3);
		ListNode node4 = new ListNode(4);
		ListNode node5 = null;
		ListNode node6 = new ListNode(6);
		ListNode node7 = new ListNode();
		node1.next = node2;
		node2.next = node3;
		node3.next = node4;
		printListFromTailToHead(node1);
		printListFromTailToHead(node5);
		printListFromTailToHead(node6);
		printListFromTailToHead(node7);
		//使用棧實現逆序列印連結串列
		printListFromTailToHeadByStack(node1);
		//使用遞迴實現逆序列印連結串列
		printListFromTailToHead(node1);
		//使用遞迴反轉實現逆序列印
		printListFromTailToHeadByReverseList(node1);
		//使用ArrayList逆序列印連結串列
		printListFromTailToHeadByArrayList(node1);
	}
	/*
	 * 方案1:通過使用棧結構,遍歷連結串列,把先遍歷的節點的值推入棧中,遍歷結束通過彈出棧內元素實現逆序列印
	 */
	public static void printListFromTailToHeadByStack(ListNode node){
		Stack<Integer> stack = new Stack<Integer>();
		while(node != null){
			stack.push(node.val);
			node = node.next;
		}
		while(!stack.isEmpty()){
			System.out.println(stack.pop()+",");
		}
	}
	/*
	 * 2.遞迴法逆序列印連結串列
	 */
	public static void printListFromTailToHead(ListNode node){
		if(node != null){
			if(node.next != null){
				printListFromTailToHead(node.next);
			}
			System.out.println(node.val+",");
		}
		else{
			System.out.println("輸入的連結串列為空");
		}
	}
	/*
	 * 3.使用ArrayList逆序列印連結串列
	 */
	public static void printListFromTailToHeadByReverseList(ListNode node){
		if(node == null){
			System.out.println("輸入連結串列為null");
		}
		ArrayList<Integer> arrayList = new ArrayList<Integer>();
		//解析:Stack<NestedInteger> stack = new Stack<NestedInteger>();
		//<>代表泛型,Stack<NestedInteger>代表該Stack中只能放入NestedInteger類或者其子類的例項。
		while(node != null){
			arrayList.add(node.val);
			node = node.next;
		}
		for(int i = arrayList.size()-1;i >= 0;i--){
			System.out.println(arrayList.get(i)+",");
		}
	}
	/*
	 * 4.遞迴反轉連結串列後遍歷列印
	 */
	public static void printListFromTailToHeadByArrayList(ListNode node){
		ListNode reversedNode = reverse(node);
		while(reversedNode != null){
			System.out.println(reversedNode.val+",");
			reversedNode = reversedNode.next;
		}
	}
	private static ListNode reverse(ListNode head){
		if(head.next == null){
			return head;
		}
		
		ListNode reversedListNode = reverse(head.next);
		head.next.next = head;
		head.next = null;
		return reversedListNode;
	}

}

輸出結果

4,
3,
2,
1,
輸入的連結串列為空
6,
0,
4,
3,
2,
1,
4,
3,
2,
1,
4,
3,
2,
1,
4,
3,
2,
1,


1.結點4 5 6 7
在這裡插入圖片描述
2.方法4 指標反轉
在這裡插入圖片描述

3.單鏈表反轉
如何把一個單鏈表進行反轉?
方法1:
將單鏈表儲存為陣列,然後按照陣列的索引逆序進行反轉。浪費空間
方法2:
使用3個指標遍歷單鏈表,逐個連結點進行反轉。
使用p和q兩個指標配合工作,使得兩個節點間的指向反向,同時用r記錄剩下的連結串列。

p = head;
q = head->next;

在這裡插入圖片描述

head->next = NULL;


現在進入迴圈體,這是第一次迴圈。

r = q->next;
q->next = p;

在這裡插入圖片描述

p = q;
q =r;

在這裡插入圖片描述
第二次迴圈。

r = q->next

在這裡插入圖片描述

q->next = p; 

在這裡插入圖片描述

p = q;

在這裡插入圖片描述

q = r

在這裡插入圖片描述
。。。。。。

方法3:
從第2個節點到第N個節點,依次逐節點插入到第1個節點(head節點)之後,最後將第一個節點挪到新表的表尾。
在這裡插入圖片描述
在這裡插入圖片描述

方法是:對於一條連結串列,從第2個節點到第N個節點,依次逐節點插入到第1個節點(head節點)之後,(N-1)次這樣的操作結束之後將第1個節點挪到新表的表尾即可。

方法4: 遞迴
(相信我們都熟悉的一點是,對於樹的大部分問題,基本可以考慮用遞迴來解決。但是我們不太熟悉的一點是,對於單鏈表的一些問題,也可以使用遞迴。可以認為單鏈表是一顆永遠只有左(右)子樹的樹,因此可以考慮用遞迴來解決。或者說,因為單鏈表本身的結構也有自相似的特點,所以可以考慮用遞迴來解決)
現在需要把A->B->C->D進行反轉,
可以先假設B->C->D已經反轉好,已經成為了D->C->B,那麼接下來要做的事情就是將D->C->B看成一個整體,讓這個整體的next指向A,所以問題轉化了反轉B->C->D。那麼,
可以先假設C->D已經反轉好,已經成為了D->C,那麼接下來要做的事情就是將D->C看成一個整體,讓這個整體的next指向B,所以問題轉化了反轉C->D。那麼,
可以先假設D(其實是D->NULL)已經反轉好,已經成為了D(其實是head->D),那麼接下來要做的事情就是將D(其實是head->D)看成一個整體,讓這個整體的next指向C,所以問題轉化了反轉D。

單向連結串列經常被設計成面試題
面試題6:從頭到尾列印連結串列
面試題18:刪除連結串列的節點
面試題22:連結串列中倒數第k個節點
面試題24:反轉連結串列
面試題25:合併兩個排序的連結串列
面試題52:兩個連結串列的第一個公共節點

參考
1.《劍指offer》
2.https://www.cnblogs.com/gl-developer/p/6438311.html
3.https://www.weiweiblog.cn/printlistfromtailtohead/
4.https://www.cnblogs.com/mafeng/p/7149980.html