[java版数据结构和算法系列之二]链表之一 --【单链】---手把手带你模拟链表的实现【内含BAT链表面试题实现】

目录

 

链表(Linked List)介绍【单链表篇】

单链表介绍

单链表模拟

1. 定义pojo

2. 定义内部类SingleLinkedList 管理我们的pojo对象,并实现增删改查方法(我们这实现按数字编号自然排序的单链表)

面试题目1:获取倒数第N个节点

面试题目2:获取倒数第N个节点

面试题3:逆序打印(这里使用栈的方式)


链表(Linked List)介绍【单链表篇】

链表包括:1.单链链表 ; 2.双链链表 ; 3. 环状链表

    链表是有序的列表,但是它在内存中是存储如下

                                         

1)链表是以节点的方式来存储,是链式存储;

2)每个节点包含 data 域, next 域:指向下一个节点;

3)如图:发现链表的各个节点不一定是连续存储;

4)  链表分带头节点的链表和没有头节点的链表,根据实际的需求来确定。

 

 

单链表介绍

      单链表(带头结点) 逻辑结构示意图如下:

     

  单链表:在结点中数据域【既图中的头、a1....aa】用来存储数据元素,指针域用于指向下一个具有相同结构的结点。因为只有一个指针结点,称为单链表。

  单链表三个概念需要区分清楚:分别是头指针,头节点和首元节点。 

                      

  • 头结点:有时,在链表的第一个结点之前会额外增设一个结点,结点的数据域一般不存放数据(有些情况下也可以存放链表的长度等信息),此结点被称为头结点。

若头结点的指针域为空(NULL),表明链表是空表。头结点对于链表来说,不是必须的,在处理某些问题时,给链表添加头结点会使问题变得简单。

  • 首元结点:链表中第一个元素所在的结点,它是头结点后边的第一个结点。

  • 头指针:永远指向链表中第一个结点的位置(如果链表有头结点,头指针指向头结点;否则,头指针指向首元结点)。

  • 头结点和头指针的区别: 1)头指针是一个指针,头指针指向链表的头结点或者首元结点;2)头结点是一个实际存在的结点,它包含有数据域和指针域;3)两者在程序中的直接体现就是:头指针只声明而没有分配存储空间,头结点进行了声明并分配了一个结点的实际物理内存。

                                           

单链表中可以没有头结点,但是不能没有头指针!
头节点的引入能使链表对第一个元素的删除和插入和其他元素相同,不用另外说明,使得代码更加简洁。

单链表的基本操作:

单链表的基本操作有:增(add),删(remove),改(set),查(find),插(insert)等。

 

单链表模拟

 我们来依次实现单链表方法:

首先,我们制定一个数据的相关信息(任意的,自己喜欢就好):我们增加的数据要包括数字编号、名称、next。

我们再指定一个头节点(不存放数据,只包含数字编号)。

好,我们来写一个类:1.数字编号、2.名称、3.next

                          这是一个类的内部类,外部的大类名为SingleLinkedListDemo

1. 定义pojo

//定义PoJoNumber , 每个PoJoNumber 对象就是一个节点
class PoJoNumber{

		public  int nums;    	 //数字编号
		public  String name; 	 //名称
		public  PoJoNumber next; //指向下一个节点的指针,类型为PoJoNumber,因为下一个节点也存储的是PoJoNumber对象
		public PoJoNumber(int nums, String name, PoJoNumber next) {
			super();
			this.nums = nums;
			this.name = name;
			this.next = next;
		}
		@Override
		public String toString() {
			return "PoJoNumber [nums=" + nums + ", name=" + name + ", next=" + next + "]";
		}

	}

2. 定义内部类SingleLinkedList 管理我们的pojo对象,并实现增删改查方法(我们这实现按数字编号自然排序的单链表)

  add方法(排序和不排序)实现:

  思路:1.先初始化一个头节点, 头节点不要动, 不存放具体的数据;

             2.添加节点到单向链表 。

                    不考虑编号顺序时add方法 :1)找到当前链表的最后节点 ;2)将最后这个节点的next 指向 新的节点。

                    考虑数字编号进行自然排序addByOrder方法:1)从头节点遍历,比较节点的数字编号,从小到大进行排序;     

             3.head节点不能动【头节点】。

             4.如果有这个排名,则添加失败,并给出提示

具体看代码实现,有详细注释:

   add方法(不排序):

//add,不排序
		 public void add(PoJoNumber poJoNumber){
			 //1.因为head节点不能动,因此我们需要一个辅助遍历 temp。为什么需要?看完代码细细品!
			 PoJoNumber temp =head;
			 //2.循环遍历
			 while(true){
				 //2.1不排序时,节点是添加到链表最后的,所以我们要找到链表最后,如果是最后就break
				 if(temp.next==null){ 
					 break;
				 }
				 temp = temp.next;	 //2.2如果当前节点不是最后就把下一个节点赋给当前节点,然后再接着循环遍历
			 }
			 //3.退出循环,说明已经循环到最后节点,把要新增的节点添加到最后
			 temp.next = poJoNumber; 
		  }

    addByOrder方法(排序):

//addByOrder,排序
		 public void addByOrder(PoJoNumber poJoNumber){
			//1.因为head节点不能动,因此我们需要一个辅助遍历 temp。为什么需要?看完代码细细品!
			 PoJoNumber temp =head;
			 boolean flag =false;//定义一个辅助标志,表示要添加的数字编号已经存在
			while(true){
				if(temp.next==null){ //说明已经到链表最后
					break;
				}
				if(poJoNumber.nums<temp.next.nums ){ //找到要添加的位置
				      break;
				}else if(poJoNumber.nums==temp.next.nums ){//说明要添加的数字编号已经存在
					flag=true;
					break;
				}
				temp = temp.next;	
			}
			if(flag){ //说明要添加的数字编号已经存在
				System.out.printf("要添加的数字编号 %d已经存在,不能重复添加",poJoNumber.nums);
			}else{ //数字不存在,可以添加
				poJoNumber.next=temp.next; //第一步
				temp.next=poJoNumber;  //第二步     注:这两步不能颠倒!
			}
		 }

list方法实现:

//list循环show列表节点数据
		 public void list(){
			 if(head.next==null){
				 System.out.println("链表节点为空!");
				 return;
			 }		
			 PoJoNumber temp =head.next; //说明头节点后面节点有数据,并赋给辅助变量temp
			 while(true){
				 if(temp==null){ //说明已经循环到链表最后
					 break;
				 }
				 
				 System.out.println(temp);//输出节点的信息	
				 temp= temp.next;     //将temp后移
			 }
		 }

 

   del删除方法实现:

 //删除方法
		 public void del(int nums){
			
			 PoJoNumber temp =head; 
			 boolean flag =false;
			 while(true){
				 if(temp.next==null){
					 break;
				 }
				 if(temp.next.nums == nums){
					 flag= true;
					 break;
				 }
				 temp = temp.next;
			 }
			if(flag){
			  temp.next=temp.next.next;
			}{
				System.out.printf("要删除的 %d 节点不存在\n", nums);
			}
		 }

 update方法实现:

public void update(PoJoNumber poJoNumber){
			 PoJoNumber temp =head; 
			 boolean flag =false;
			 while(true){
				 if(temp.next==null){
					 break;
				 }
				 if(temp.next.nums == poJoNumber.nums){
					 flag= true;
					 break;
				 }
				 temp = temp.next;
			 }
			if(flag){
			  temp.next.name=poJoNumber.name;
			}else{
				System.out.printf("要修改的的 %d 节点不存在\n", poJoNumber.nums);
			}
			 
		 }

getLength获取链表有效节点数:

public static int getLength(PoJoNumber head){
			 if(head.next==null){
				 System.out.println("链表节点数为0");
				 return 0;
			 }
			 int counts=1;
			 PoJoNumber temp =head.next;
			 while(true){
				 if(temp.next==null){
					 return counts;
				 }
				 temp = temp.next;
				 counts++;
			 }
			 
			 
		 }

面试题目1:获取倒数第N个节点

   解题思路: 1.循环遍历获取整个链表的长度size,即上面的getLength()方法;2.获取第(size-n)个节点

public static PoJoNumber getLastIndexNode(PoJoNumber head, int index){
		if(head.next==null){
			System.out.println("链表为空");
			return null;
		}
		int size = getLength(head);//第一个遍历得到链表的长度(节点个数)
		if(index>size||index<=0){
			System.out.println("查找倒数弟N个节点: 输入的index错误!");
			return null;
		}
		PoJoNumber temp= head.next;
	    for(int i=0;i<size-index;i++){
	    	temp =temp.next;
	    }
		return temp;
	}

面试题目2:获取倒数第N个节点

     

public static void reversetList(PoJoNumber head) {
		if(head.next==null||head.next.next==null){
			return ;//如果当前链表为空,或者只有一个节点,无需反转,直接返回
		}
		PoJoNumber temp = head.next;
		PoJoNumber newHead = new SingleLinkedListDemo().new PoJoNumber(0, "");
		PoJoNumber newNext =null; // 指向当前节点[temp]的下一个节点
		while(temp!=null){
			//1.获取原链表第一个节点的后一个节点,赋值给newNext
			newNext =temp.next;
			//2.将新链表的元始节点,赋值给temp.next
			temp.next =newHead.next;
			//3.元始节点指向当前temp
			newHead.next=temp;
			//4.temp后移一位
			temp=newNext;
		}
		//5.将原链表head.next指向newHead.next
		head.next = newHead.next;
	}

面试题3:逆序打印(这里使用栈的方式)

      push:进栈     pop:出栈

      栈:先进后出,后进先出

	public static void reversePrint(PoJoNumber head){
		if(head.next==null){
			System.out.println("空链表!");
			return;
		}
		PoJoNumber temp = head.next;
		Stack<PoJoNumber> stack = new Stack<PoJoNumber>();
		while(temp!=null){		
			stack.push(temp);
			temp =temp.next;
		}
		
		while(stack.size() > 0){
			System.out.println(stack.pop());
		}
		
	}

/***************************************************************************************************************************/

单链表以及面试题解答源码

package com.study.linked;

import java.util.Stack;

/*
 * 模拟链表
 * 1.模拟实现链表增、删、查、改等方法;
 * 2.获取链表节点个数;
 * 3.获取链表倒数第N个节点
 * 4.实现单链表的反转;
 * 5.从尾到头打印单链表 【两种方式  方式1:反向遍历 ; 方式2:Stack栈】
 * 
 */
public class SingleLinkedListDemo {

	
	public static void main(String[] args) {
		SingleLinkedListDemo demo = new SingleLinkedListDemo();
		
		PoJoNumber member1 =demo.new PoJoNumber(1, "张三");
		PoJoNumber member2 =demo.new PoJoNumber(2, "李四");
		PoJoNumber member3 =demo.new PoJoNumber(3, "王麻子");
		PoJoNumber member4 =demo.new PoJoNumber(4, "赵老五");
		
		SingleLinkedList link =demo.new SingleLinkedList();
		//不排序add
	/*	System.out.println("不排序add:");
		  link.add(member1);
		  link.add(member2);
		  link.add(member3);
		  link.add(member4);
		  link.list();
		  */
		//排序addByOrder
			System.out.println("排序addByOrder:");  
			  link.addByOrder(member1);
			  link.addByOrder(member4);
			  link.addByOrder(member3);
			  link.addByOrder(member2);
			  link.list();
			  link.del(4);
			  System.out.println("del删除4后:");
			  link.list();
              link.update(demo.new PoJoNumber(2, "李五"));
              System.out.println("update 修改2后:");
              link.list();
              System.out.println("链表有效节点数:"+getLength(link.getHead()));
              
              //获取倒数第N个节点
              System.out.println("获取倒数第N个节点:");
              System.out.println(getLastIndexNode(link.getHead(), 2));
              
              //将链表反转
              reversetList(link.getHead());
              System.out.println("链表反转后:");
              link.list();
              
              //逆序打印。栈法
              System.out.println("逆序打印链表:");
              reversePrint(link.getHead());

	}
	
	
	
	
	
	
	
	
	class SingleLinkedList{
		
		private PoJoNumber  head= new PoJoNumber(0,"");//定义一个头节点,不存放数据,只有数字编号0
	
		public PoJoNumber getHead() { //main内获取head节点信息
			return head;
		}
		/*******************add******************************/ 
		//add,不排序
		 public void add(PoJoNumber poJoNumber){
			 //1.因为head节点不能动,因此我们需要一个辅助遍历 temp。为什么需要?看完代码细细品!
			 PoJoNumber temp =head;
			 //2.循环遍历
			 while(true){
				 //2.1不排序时,节点是添加到链表最后的,所以我们要找到链表最后,如果是最后就break
				 if(temp.next==null){ 
					 break;
				 }
				 temp = temp.next;	 //2.2如果当前节点不是最后就把下一个节点赋给当前节点,然后再接着循环遍历
			 }
			 //3.退出循环,说明已经循环到最后节点,把要新增的节点添加到最后
			 temp.next = poJoNumber; 
		  }
		 
		 
		//addByOrder,排序
		 public void addByOrder(PoJoNumber poJoNumber){
			//1.因为head节点不能动,因此我们需要一个辅助遍历 temp。为什么需要?看完代码细细品!
			 PoJoNumber temp =head;
			 boolean flag =false;//定义一个辅助标志,表示要添加的数字编号已经存在
			while(true){
				if(temp.next==null){ //说明已经到链表最后
					break;
				}
				if(poJoNumber.nums<temp.next.nums ){ //找到要添加的位置
				      break;
				}else if(poJoNumber.nums==temp.next.nums ){//说明要添加的数字编号已经存在
					flag=true;
					break;
				}
				temp = temp.next;	
			}
			if(flag){ //说明要添加的数字编号已经存在
				System.out.printf("要添加的数字编号 %d已经存在,不能重复添加",poJoNumber.nums);
			}else{ //数字不存在,可以添加
				poJoNumber.next=temp.next; //第一步
				temp.next=poJoNumber;  //第二步     注:这两步不能颠倒!
			}
		 }
		
		/********************分割符******************************/ 
		 
		 /********************list方法******************************/ 
		 
		//list循环show列表节点数据
		 public void list(){
			 if(head.next==null){
				 System.out.println("链表节点为空!");
				 return;
			 }		
			 PoJoNumber temp =head.next; //说明头节点后面节点有数据,并赋给辅助变量temp
			 while(true){
				 if(temp==null){ //说明已经循环到链表最后
					 break;
				 }
				 
				 System.out.println(temp);//输出节点的信息	
				 temp= temp.next;     //将temp后移
			 }
		 }
		 
		/********************分割符******************************/ 
		 
		 /********************del方法******************************/ 
		 //删除方法
		 public void del(int nums){
			
			 PoJoNumber temp =head; 
			 boolean flag =false;
			 while(true){
				 if(temp.next==null){
					 break;
				 }
				 if(temp.next.nums == nums){
					 flag= true;
					 break;
				 }
				 temp = temp.next;
			 }
			if(flag){
			  temp.next=temp.next.next;
			}else{
				System.out.printf("要删除的 %d 节点不存在\n", nums);
			}
		 }
	
		 /********************分隔符******************************/  
		 
		 /********************update修改方法******************************/
		 public void update(PoJoNumber poJoNumber){
			 PoJoNumber temp =head; 
			 boolean flag =false;
			 while(true){
				 if(temp.next==null){
					 break;
				 }
				 if(temp.next.nums == poJoNumber.nums){
					 flag= true;
					 break;
				 }
				 temp = temp.next;
			 }
			if(flag){
			  temp.next.name=poJoNumber.name;
			}else{
				System.out.printf("要修改的的 %d 节点不存在\n", poJoNumber.nums);
			}
			 
		 }
		 
		 /********************分隔符******************************/   
		 
		 
	

		 /********************分隔符******************************/ 		 
		 
	}
	
	 /********************getLength获取链表节点数***************/   
	 public static int getLength(PoJoNumber head){
		 if(head.next==null){
			 System.out.println("链表节点数为0");
			 return 0;
		 }
		 int counts=1;
		 PoJoNumber temp =head.next;
		 while(true){
			 if(temp.next==null){
				 return counts;
			 }
			 temp = temp.next;
			 counts++;
		 }
		 
		 
	 }
	
	
	 /*******************面试题目1:获取倒数第N个节点******************************/ 		
	public static PoJoNumber getLastIndexNode(PoJoNumber head, int index){
		if(head.next==null){
			System.out.println("链表为空");
			return null;
		}
		int size = getLength(head);//第一个遍历得到链表的长度(节点个数)
		if(index>size||index<=0){
			System.out.println("查找倒数弟N个节点: 输入的index错误!");
			return null;
		}
		PoJoNumber temp= head.next;
	    for(int i=0;i<size-index;i++){
	    	temp =temp.next;
	    }
		return temp;
	}
	 /*******************面试题目1----end******************************/ 
	
	
	
	
	
	/*******************面试题目2:实现单链表的反转 ******************************/ 
	public static void reversetList(PoJoNumber head) {
		if(head.next==null||head.next.next==null){
			return ;//如果当前链表为空,或者只有一个节点,无需反转,直接返回
		}
		PoJoNumber temp = head.next;
		PoJoNumber newHead = new SingleLinkedListDemo().new PoJoNumber(0, "");
		PoJoNumber newNext =null; // 指向当前节点[temp]的下一个节点
		PoJoNumber newTemp =null;
		while(temp!=null){
			//1.获取原链表第一个节点的后一个节点,赋值给newNext
			newNext =temp.next;
			//2.将新链表的元始节点,赋值给newtemp
			newTemp =newHead.next;
			//3.元始节点指向当前temp
			newHead.next=temp;
			temp.next= newTemp;
			//4.temp后移一位
			temp=newNext;
		}
		//5.将原链表head.next指向newHead.next
		head.next = newHead.next;
	}
	
	/*******************面试题目2----end******************************/ 
	
	
	/*******************面试题目3----逆序打印链表******************************/ 
	//逆序打印链表
	public static void reversePrint(PoJoNumber head){
		if(head.next==null){
			System.out.println("空链表!");
			return;
		}
		PoJoNumber temp = head.next;
		Stack<PoJoNumber> stack = new Stack<PoJoNumber>();
		while(temp!=null){		
			stack.push(temp);
			temp =temp.next;
		}
		
		while(stack.size() > 0){
			System.out.println(stack.pop());
		}
		
	}
	/*******************面试题目3----end******************************/ 
	
	class PoJoNumber{

		public int nums;    	 //数字编号
		public String name; 	 //名称
		public PoJoNumber next; //指向下一个节点的指针,类型为PoJoNumber,因为下一个节点也存储的是PoJoNumber对象
		public PoJoNumber(int nums, String name) {
			super();
			this.nums = nums;
			this.name = name;
		}
		@Override
		public String toString() {
			return "PoJoNumber [nums=" + nums + ", name=" + name + ", next=" + next + "]";
		}

	}
	
}

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值