主要内容:
本次练习包括删除链表中的重复数据、找出单链表中倒数第K个元素、链表反转、从尾到头输出单链表、寻找单链表的中间节点。
package com.sf.linkedlist;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
/**
* Created by laxe on 2016/11/30.
*/
public class LinkedListDemo1 {
static Node head=null;
public static void main(String args[]){
head=new Node(9);
Node next1=new Node(3);
Node next2=new Node(5);
Node next3=new Node(4);
Node next4=new Node(10);
head.next=next1;
next1.next=next2;
next2.next=next3;
next3.next=next4;
showList(head);
//deleteDuplecate();//删除重复元素
//deleteDuplecate1();
//Node key=findElem(1);//找到倒数第k个元素
//System.out.println(key.val);
//reverseIteratively();//反转链表
//printListReversely(head);//逆序输出
List<Node> list=findMid();//找到单链表的中间节点
for(Node node:list) {
System.out.println(node.val);
}
showList(head);
}
//删除链表中的重复元素
/**
* 思路一:遍历链表,把遍历到的值存储到一个hashtable中,在遍历的过程中,如果发现该节点的值已经存在,那么删除该节点。
*
* */
public static void deleteDuplecate(){
if(head==null)
return;
Node tmp=head;
Node pre=null;
HashMap<Integer,Integer> hm=new HashMap<Integer, Integer>();
while(tmp!=null){
if(!hm.containsKey(tmp.val)){
hm.put(tmp.val,1);
pre=tmp;
}else{
pre.next=tmp.next;
}
tmp=tmp.next;
}
}
/**
* 思路二:
* 双重循环遍历:
* 外循环正常遍历链表,假设外循环当前遍历的元素是curr,
* 内循环从curr开始遍历,看后面有没有和curr值一样的元素节点。如果有,那么删除
*
* */
public static void deleteDuplecate1(){
Node curr=head;
while(curr!=null){
Node tmp=curr;
while(tmp.next!=null){
if(tmp.next.val==curr.val){
tmp.next=tmp.next.next;
}else{
tmp=tmp.next;
}
}
curr=curr.next;
}
}
/**
* 思路三:
* 双重循环遍历:
* 外循环正常遍历链表,假设外循环当前遍历的元素是curr,
* 内循环从head开始遍历,看后面有没有和curr值一样的元素节点。如果有,那么删除,内循环结束,因为前面与curr节点值相同的元素只会有一个。
*
* */
//TODO
//如何找出单链表中倒数第K个元素
/**
* 思路1:遍历一遍链表求出链表长度n,找到倒数第k个,就是找到正数第n-k个。差不多进行了两次遍历
* 思路2:从某个元素开始,遍历k个元素后正好到达链表尾,那么该元素就是要找的第k个元素。
* 算法设计:从头开始,依次对链表的每一个节点元素进行测试,遍历k个元素后若达到链表尾,那么就找到了目标元素,这种方式也不是最优的。
* 思路3:设置两个指针,两个指针相距k-1,并且同时往后移动,直到后面那个元素达到末尾,那么前面那个元素就是目标元素。只遍历一遍。
*
* */
public static Node findElem(int k){
if(k<1 || k>length())
return null;
Node first=head;
Node second=head;
for(int i=0;i<k-1;i++){
second=second.next;
}
while(second.next!=null){
first=first.next;
second=second.next;
}
return first;
}
//实现链表的反转
/**
* 假设有i,m,n三个节点。i->m->n
* 那么在反转的过程中就是把指针箭头反转。
* 如果将m->i,那么就没有指针指向n了,链表就断开了,所以,在反转的过程中需要在调整m的next之前,需要把n保存下来。
* */
public static void reverseIteratively(){
Node pReversedHead=head;
Node pNode=head;
Node pPrev=null;
while(pNode!=null){
Node pNext=pNode.next;
if(pNext==null)
pReversedHead=pNode;
pNode.next=pPrev;
pPrev=pNode;
pNode=pNext;
}
head=pReversedHead;
}
//从尾到头输出单链表
public static void printListReversely(Node head){
if(head!=null){
printListReversely(head.next);
System.out.println(head.val);
}
}
//找到链表的中间元素
/**
* 思路1:第一次遍历,得出链表长度;第二次遍历,根据索引遍历到中间元素
* 思路2:设置两个指针,一个快指针,一个慢指针。快指针每次走两步,慢指针一次走一步。
* 当快指针达到链表尾时,慢指针正好到达中间
* 链表长度若为偶数,则慢指针指向的元素和下一个元素都是中间元素;
* 链表长度若为奇数,则慢指针指向的元素就是中间元素;
*
* */
public static List<Node> findMid(){
Node fast=head;
Node slow=head;
List<Node> list=new ArrayList<Node>();
while(fast!=null && fast.next!=null && fast.next.next!=null){
fast=fast.next.next;
slow=slow.next;
}
if(length()%2==0) {
list.add(slow);
list.add(slow.next);
}else{
list.add(slow);
}
return list;
}
/**
* 打印链表
* */
public static void showList(Node head){
if(head==null)
return;
Node tmp=head;
while(tmp!=null)
{
if(tmp.next!=null) {
System.out.print(tmp.val+"->");
tmp = tmp.next;
}else{
System.out.print(tmp.val);
tmp = tmp.next;
}
}
System.out.println();
}
/**
* 获取链表的长度
* */
public static int length(){
int length=0;
Node tmp=head;
while(tmp!=null){
length++;
tmp=tmp.next;
}
return length;
}
}