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

目录

链表(Linked List)介绍【单链表篇】
单链表介绍
单链表模拟
1. 定义pojo
2. 定义内部类SingleLinkedList 管理我们的pojo对象,并实现增删改查方法(我们这实现按数字编号自然排序的单链表)
面试题目1:获取倒数第N个节点
面试题目2:获取倒数第N个节点
面试题3:逆序打印(这里使用栈的方式)
链表(Linked List)介绍【单链表篇】 链表包括:1.单链链表 ; 2.双链链表 ; 3. 环状链表
链表是有序的列表,但是它在内存中是存储如下
[java版数据结构和算法系列之二]链表之一 --【单链】---手把手带你模拟链表的实现【内含BAT链表面试题实现】
文章图片

1)链表是以节点的方式来存储,是链式存储;
2)每个节点包含 data 域, next 域:指向下一个节点;
3)如图:发现链表的各个节点不一定是连续存储;
4)链表分带头节点的链表和没有头节点的链表,根据实际的需求来确定。


单链表介绍 单链表(带头结点) 逻辑结构示意图如下:
[java版数据结构和算法系列之二]链表之一 --【单链】---手把手带你模拟链表的实现【内含BAT链表面试题实现】
文章图片

单链表:在结点中数据域【既图中的头、a1....aa】用来存储数据元素,指针域用于指向下一个具有相同结构的结点。因为只有一个指针结点,称为单链表。
单链表三个概念需要区分清楚:分别是头指针,头节点和首元节点。
[java版数据结构和算法系列之二]链表之一 --【单链】---手把手带你模拟链表的实现【内含BAT链表面试题实现】
文章图片

  • 头结点:有时,在链表的第一个结点之前会额外增设一个结点,结点的数据域一般不存放数据(有些情况下也可以存放链表的长度等信息),此结点被称为头结点。
若头结点的指针域为空(NULL),表明链表是空表。头结点对于链表来说,不是必须的,在处理某些问题时,给链表添加头结点会使问题变得简单。
  • 首元结点:链表中第一个元素所在的结点,它是头结点后边的第一个结点。
  • 头指针:永远指向链表中第一个结点的位置(如果链表有头结点,头指针指向头结点;否则,头指针指向首元结点)。
  • 头结点和头指针的区别: 1)头指针是一个指针,头指针指向链表的头结点或者首元结点;2)头结点是一个实际存在的结点,它包含有数据域和指针域;3)两者在程序中的直接体现就是:头指针只声明而没有分配存储空间,头结点进行了声明并分配了一个结点的实际物理内存。
[java版数据结构和算法系列之二]链表之一 --【单链】---手把手带你模拟链表的实现【内含BAT链表面试题实现】
文章图片

单链表中可以没有头结点,但是不能没有头指针!
头节点的引入能使链表对第一个元素的删除和插入和其他元素相同,不用另外说明,使得代码更加简洁。
单链表的基本操作:
单链表的基本操作有:增(add),删(remove),改(set),查(find),插(insert)等。

单链表模拟
我们来依次实现单链表方法:
首先,我们制定一个数据的相关信息(任意的,自己喜欢就好):我们增加的数据要包括数字编号、名称、next。
我们再指定一个头节点(不存放数据,只包含数字编号)。
好,我们来写一个类:1.数字编号、2.名称、3.next
这是一个类的内部类,外部的大类名为SingleLinkedListDemo
1. 定义pojo
//定义PoJoNumber , 每个PoJoNumber 对象就是一个节点 class PoJoNumber{publicint nums; //数字编号 publicString name; //名称 publicPoJoNumber 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

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

面试题目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 stack = new Stack(); 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 PoJoNumberhead= 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.numssize||index<=0){ System.out.println("查找倒数弟N个节点: 输入的index错误!"); return null; } PoJoNumber temp= head.next; for(int i=0; i stack = new Stack(); 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 + "]"; } } }

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

    推荐阅读