数据结构
第一章 数组和队列
一. 稀疏数组
1.基本介绍:
当一个数组中大部分元素是0,或者是同一个值得数组时,可以用稀疏数组来保存该数组
2.处理方法:
1.记录数组一共有几行几列,有多少个不同得值
2.把具有不同值的元素的行列及值记录在一个小规模的数组中,从而缩小程序的规模
例:
3.二维数组转稀疏数组的思路
1.遍历 原始的二维数组,得到有效的数据的个数sum
2.根据sum就可以创建稀疏数组sparseArr int[sum+1][3]
3.将二维数组的有效数据存入到稀疏数组中
4.稀疏数组转原始的二维数组
1.读取稀疏数组的第一行的,根据第一行的数据创建原始二维数组,比如chessArr=int[6][7]
2.再读取稀疏数组后几行的数据,并赋值给原始的二维数组
public class SparseArray {
public static void main(String[] args) {
//创建一个原始的二维数组
//0:没有子 1:表述黑子 2:表示白字
int chessArr[][] = new int[11][11];
chessArr[1][2] = 1;
chessArr[2][3] = 2;
chessArr[4][5] = 2;
//输出原始二维数组
System.out.println("--------原始数组-------");
printf(chessArr);
//二维数组转稀疏数组
int spareArr[][] = arrayToSpare(chessArr);
//输出稀疏数组
System.out.println("--------稀疏数组-------");
printf(spareArr);
//输出原始二维数组
System.out.println("--------原始数组-------");
int arr[][] = spareToArray(spareArr);
printf(arr);
}
/*
* 二维数组转稀疏数组
*/
private static int[][] arrayToSpare(int chessArr[][]) {
int sum = 0;
//1.遍历 原始的二维数组,得到有效的数据的个数sum
for (int[] row : chessArr) {
for (int data : row) {
if (0 != data) {
sum++;
}
}
}
//初始化稀疏数组
int spareArr[][] = new int[sum + 1][3];
//给稀疏数组赋值
//获取行数
spareArr[0][0] = chessArr.length;
//获取列数
spareArr[0][1] = chessArr[0].length;
spareArr[0][2] = sum;
//用于记录第几个非0数据
int count = 0;
for (int i = 0; i < chessArr.length; i++) {
for (int j = 0; j < chessArr[i].length; j++) {
if (0 != chessArr[i][j]) {
count++;
spareArr[count][0] = i;
spareArr[count][1] = j;
spareArr[count][2] = chessArr[i][j];
}
}
}
return spareArr;
}
/*
* 稀疏数组转二维数组
*/
private static int[][] spareToArray(int spareArr[][]) {
//获取稀疏数组第一行
//二维数组的行数
int row =spareArr[0][0];
//二维数组的列数
int col =spareArr[0][1];
//原始二维数组
int array[][]= new int[row][col];
for(int i = 1; i < spareArr.length; i++){
//获取有数据的行
int rowNum= spareArr[i][0];
//获取有数据的列
int colNum= spareArr[i][1];
array[rowNum][colNum]=spareArr[i][2];
}
return array;
}
/*
* 打印二维数组
*/
private static void printf(int arr[][]) {
for (int[] row : arr) {
for (int data : row) {
System.out.printf("%d\t", data);
}
System.out.println();
}
}
}
输出:
--------原始数组-------
0 0 0 0 0 0 0 0 0 0 0
0 0 1 0 0 0 0 0 0 0 0
0 0 0 2 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 2 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0
--------稀疏数组-------
11 11 3
1 2 1
2 3 2
4 5 2
--------原始数组-------
0 0 0 0 0 0 0 0 0 0 0
0 0 1 0 0 0 0 0 0 0 0
0 0 0 2 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 2 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0
二.队列
1.队列介绍
队列是一个有序的列表,可以用数组和链表来实现,遵循先入先出的原则
2.数组模拟队列
1.队列的输出,输入分别从前后端来处理,因此需要两个变量front及rear记录队列的前后端,
front会随着数据的输出而改变,rear随着数据的输入而改变
存队列:
1.将尾指针往后移:rear+1 front==rear 空
2.若尾指针rear小于队列的最大下标maxSize-1,存入数组,若rear==maxSize-1队列满
/*
* 使用数组模拟队列
*/
class Array {
//数组的最大容量
private int maxSize;
//对头的位置
private int front;
//队尾的位置
private int rear;
//该数组用于模拟队列
private int[] arr;
//创建队列的构造器
public Array(int size) {
maxSize = size;
arr = new int[maxSize];
front = -1; //指向队列的头部,front指向队列头的前一个位置
rear = -1;//指向队列的尾部,rear指向队列尾的位置
}
//判断队列是否为满
public boolean isFull() {
return rear == maxSize - 1;
}
//判断队列是否为空
public boolean isEmpty() {
return rear == front;
}
//添加队列
public void addQueue(int n) {
//判断队列是否为满
if (isFull()) {
System.out.println("----满队列---");
return;
}
//rear后移
rear++;
arr[rear] = n;
}
//出队列
public int popQueue() {
//判断队列是否为空
if (isEmpty()) {
throw new RuntimeException("----空队列---");
}
//front后移
front++;
return arr[front];
}
//显示队列的所有数据
public void showQueue() {
//判断队列是否为空
if (isEmpty()) {
throw new RuntimeException("----空队列---");
}
for (int i = front+1; i < maxSize; i++) {
System.out.printf("arr[%d]=%d\n", i, arr[i]);
}
}
//显示队列的头数据
public int headQueue() {
if (isEmpty()) {
throw new RuntimeException("----空队列---");
}
return arr[front + 1];
}
}
问题:
1.数组只能使用一次,没有达到复用的效果
2.将这个数组使用算法,改成一个环形的数组 %
3.数组模拟环形队列
思路:
1.front变量的含义做一个调整:front指向队列的第一个元素(arr[front] 队列的第一个元素)
2.rear变量的含义做一个调整:rear指向队列指向最后一个元素的后一个位置.(空出一个空间做出约定)
3.当队列满时: (rear+1)%maxSize=front
4.当队列为空时:front=rear
5.队列中有效的数据的个数 (rear+maxSize-front)%maxSize
class CircleArray {
//数组的最大容量
private int maxSize;
//对头的位置
private int front;
//队尾的位置
private int rear;
//该数组用于模拟队列
private int[] arr;
//创建队列的构造器
public CircleArray(int size) {
maxSize = size;
arr = new int[maxSize];
front = 0; //指向队列的头部,front指向队列头的位置
rear = 0;//指向队列的尾部,rear指向队列尾的后一个位置
}
//判断队列是否为满
public boolean isFull() {
return (rear + 1) % maxSize == front;
}
//判断队列是否为空
public boolean isEmpty() {
return rear == front;
}
//添加队列
public void addQueue(int n) {
//判断队列是否为满
if (isFull()) {
System.out.println("----满队列---");
return;
}
//rear后移
arr[rear] = n;
rear = (rear + 1) % maxSize;
}
//出队列
public int popQueue() {
//判断队列是否为空
if (isEmpty()) {
throw new RuntimeException("----空队列---");
}
//front后移
//1.先把front对应的值保留到一个临时变量
//2.将front后移
//3.将临时变量返回。
int value = arr[front];
front = (front + 1) % maxSize;
return value;
}
//显示队列的所有数据
public void showQueue() {
//判断队列是否为空
if (isEmpty()) {
throw new RuntimeException("----空队列---");
}
//思路:从front开始遍历,遍历多少个元素
for (int i = front; i < front + size(); i++) {
System.out.printf("arr[%d]=%d\n", i % maxSize, arr[i % maxSize]);
}
}
//当前队列有效数据的个数
public int size() {
//rear = 1;
//front = 0;
//maxSize=3
return (rear + maxSize - front) % maxSize;
}
//显示队列的头数据
public int headQueue() {
if (isEmpty()) {
throw new RuntimeException("----空队列---");
}
return arr[front];
}
}
三.单链表
1.链表是以节点的方式来存储的,是链式存储的
2.每个节点包含data域,next域
3.链表的各个节点不一定是连续存放的
4.链表分带头节点的链表和没有头节点的链表
1.添加(创建)
1.先创建一个head头节点,表示单链表的头
2.后面每添加一个节点,就直接加到链表的最后
遍历:
1.通过一个辅助遍历,帮助遍历整个链表
package com.hyy.linkedList;
/**
* @Author:
* @Description:
* @Date:2021/1/8
* @Modified By:
*/
public class SingleLinkedListDemo {
public static void main(String[] args) {
SingleLinkedList singleLinkedList =new SingleLinkedList();
HeroNode hero1 =new HeroNode(1,"宋江","及时雨");
HeroNode hero2 =new HeroNode(2,"卢俊义","玉麒麟");
HeroNode hero3 =new HeroNode(3,"吴用","智多星");
HeroNode hero4 =new HeroNode(4,"林冲","豹子头");
singleLinkedList.add(hero1);
singleLinkedList.add(hero3);
singleLinkedList.add(hero4);
singleLinkedList.add(hero2);
singleLinkedList.list();
}
}
/*
* 定义一个SingleLinkedList
*/
class SingleLinkedList{
//先初始化一个头节点,头节点不要动
private HeroNode head=new HeroNode(0,"","");
//添加节点到单项链表
//思路,当不考虑编号的顺序时,
// 1.找到当前链表的最后节点
// 2.将最后的节点的next指向新的节点
public void add(HeroNode node){
//因为不能动头节点,因此需要一个辅助节点
HeroNode temp =head;
//遍历链表
while (true){
if(temp.next==null){
break;
}
//将链表后移
temp=temp.next;
}
//当退出while循环时,temp指向了链表的最后
temp.next = node;
}
//显示链表
public void list(){
if(head.next==null){
return;
}
//因为不能动头节点,因此需要一个辅助节点
HeroNode temp =head.next;
while (true) {
if(temp==null){
break;
}
System.out.println(temp.toString());
//将链表后移
temp=temp.next;
}
}
}
/*
* 定义一个HeroNode,每个HeroNode对象就是一个头节点
*/
class HeroNode{
public int no;
public String name;
public String nickName;
public HeroNode next;
//构造器
public HeroNode(int hNo,String name,String nickName){
this.no = hNo;
this.name = name;
this.nickName = nickName;
}
@Override
public String toString() {
return "HeroNode{" +
"no=" + no +
", name=" + name +
", nickName=" + nickName +
'}';
}
------------打印-----------
HeroNode{no=1, name=宋江, nickName=及时雨}
HeroNode{no=3, name=吴用, nickName=智多星}
HeroNode{no=4, name=林冲, nickName=豹子头}
HeroNode{no=2, name=卢俊义, nickName=玉麒麟}
// 添加节点到单项链表
// 思路,当考虑编号的顺序时,
// 1.找到当前链表节点的下一个节点编号大于插入的编号的节点
// 2.将节点放在该节点的下一个位置上
public void addByOrder(HeroNode node){
//因为不能动头节点,因此需要一个辅助节点
HeroNode temp =head;
boolean flag =false;
//遍历链表
while (true){
if(temp.next==null){
break;
}
//判断编号
if(temp.next.no>node.no){
break;
}else if(temp.next.no==node.no){
flag = true;
break;
}
//将链表后移
temp=temp.next;
}
if (flag) {
//将node插入在temp的下一个节点中
System.out.printf("编号%d存在,不能插入\n",node.no);
return;
}
//当退出while循环时,temp指向了链表的最后
node.next=temp.next;
temp.next = node;
}
singleLinkedList.addByOrder(hero1);
singleLinkedList.addByOrder(hero3);
singleLinkedList.addByOrder(hero4);
singleLinkedList.addByOrder(hero2);
------------打印-----------
HeroNode{no=1, name=宋江, nickName=及时雨}
HeroNode{no=2, name=卢俊义, nickName=玉麒麟}
HeroNode{no=3, name=吴用, nickName=智多星}
HeroNode{no=4, name=林冲, nickName=豹子头}
2.修改单链表
//修改,根据编号,修改名称和昵称
public void update(HeroNode node){
HeroNode temp = head.next;
boolean flag = false;
while (true) {
if(temp==null){
break;
}
//存在编号相同的
if(temp.no==node.no){
flag = true;
break;
}
temp=temp.next;
}
if(flag){
temp.name=node.name;
temp.nickName=node.nickName;
}else {
System.out.printf("不存在编号为%d的英雄\n",node.no);
}
}
HeroNode hero5 =new HeroNode(2,"李逵","黑旋风");
singleLinkedList.update(hero5);
------------打印-----------
HeroNode{no=1, name=宋江, nickName=及时雨}
HeroNode{no=2, name=卢俊义, nickName=玉麒麟}
HeroNode{no=3, name=吴用, nickName=智多星}
HeroNode{no=4, name=林冲, nickName=豹子头}
--------修改后的链表---------
HeroNode{no=1, name=宋江, nickName=及时雨}
HeroNode{no=2, name=李逵, nickName=黑旋风}
HeroNode{no=3, name=吴用, nickName=智多星}
HeroNode{no=4, name=林冲, nickName=豹子头}
3.删除
//删除,删除指定no的英雄节点
public void delete(int no){
HeroNode temp = head;
//找到节点位置
boolean flag =false;
while (true) {
if(temp.next == null){
break;
}
if(temp.next.no == no){
flag=true;
break;
}
temp = temp.next;
}
if (flag) {
temp.next=temp.next.next;
}else {
System.out.printf("不存在编号为%d的英雄\n",no);
}
}
//删除
singleLinkedList.delete(4);
singleLinkedList.list();
HeroNode{no=1, name=宋江, nickName=及时雨}
HeroNode{no=2, name=卢俊义, nickName=玉麒麟}
HeroNode{no=3, name=吴用, nickName=智多星}
HeroNode{no=4, name=林冲, nickName=豹子头}
--------删除后的链表---------
HeroNode{no=1, name=宋江, nickName=及时雨}
HeroNode{no=2, name=卢俊义, nickName=玉麒麟}
HeroNode{no=3, name=吴用, nickName=智多星}
面试题
1.求单链表节点个数
//获取单链表节点个数(带头节点的链表,需要不统计头节点)
/**
*
* @param head 链表的头节点
* @return 有效节点的个数
*/
public static int getLength(HeroNode head){
if(head.next==null){
return 0;
}
int length=0;
HeroNode cur =head.next;
while (cur!=null){
length++;
cur = cur.next;
}
return length;
}
2.查找链表倒数第k个节点
//查找链表倒数第k个节点
public static HeroNode getNode(int k,HeroNode head){
int length = getLength(head);
if(length<k){
System.out.printf("不存在倒数第%k个节点",k);
}
int n=length-k+1;
HeroNode temp =head;
for (int i = 0; i < n; i++) {
temp=temp.next;
}
return temp;
}
HeroNode node = getNode(2, singleLinkedList.getHead());
------------------------
HeroNode{no=1, name=宋江, nickName=及时雨}
HeroNode{no=2, name=卢俊义, nickName=玉麒麟}
HeroNode{no=3, name=吴用, nickName=智多星}
HeroNode{no=4, name=林冲, nickName=豹子头}
HeroNode{no=5, name=李逵, nickName=黑旋风}
有效节点个数:5
HeroNode{no=4, name=林冲, nickName=豹子头}
3.单链表的反转
//反转链表
public static void reverse(HeroNode head){
//反转链表
HeroNode reverse = new HeroNode(0,"","");
//循环的temp节点
HeroNode temp = head.next;
//存储的节点
HeroNode t=null;
while (temp !=null){
t = temp;
temp=temp.next;
t.next=reverse.next;
reverse.next=t;
}
//把头节点交换
head.next=reverse.next;
}
reverse(singleLinkedList.getHead());
singleLinkedList.list();
HeroNode{no=1, name=宋江, nickName=及时雨}
HeroNode{no=2, name=卢俊义, nickName=玉麒麟}
HeroNode{no=3, name=吴用, nickName=智多星}
HeroNode{no=4, name=林冲, nickName=豹子头}
HeroNode{no=5, name=李逵, nickName=黑旋风}
--------反转后的链表---------
HeroNode{no=5, name=李逵, nickName=黑旋风}
HeroNode{no=4, name=林冲, nickName=豹子头}
HeroNode{no=3, name=吴用, nickName=智多星}
HeroNode{no=2, name=卢俊义, nickName=玉麒麟}
HeroNode{no=1, name=宋江, nickName=及时雨}
4.从尾到头打印单链表
思路:利用栈这个数据结构
//从尾到头打印链表
public static void reversePrint(HeroNode head) {
Stack<HeroNode> stack = new Stack<>();
HeroNode temp = head.next;
while (temp != null) {
stack.push(temp);
temp = temp.next;
}
while (stack.size() > 0) {
HeroNode pop = stack.pop();
System.out.println(pop);
}
}
System.out.println("--------逆序打印链表---------");
reversePrint(singleLinkedList.getHead());
------------------------
HeroNode{no=1, name=宋江, nickName=及时雨}
HeroNode{no=3, name=吴用, nickName=智多星}
HeroNode{no=4, name=林冲, nickName=豹子头}
HeroNode{no=2, name=卢俊义, nickName=玉麒麟}
HeroNode{no=5, name=李逵, nickName=黑旋风}
--------逆序打印链表---------
HeroNode{no=5, name=李逵, nickName=黑旋风}
HeroNode{no=2, name=卢俊义, nickName=玉麒麟}
HeroNode{no=4, name=林冲, nickName=豹子头}
HeroNode{no=3, name=吴用, nickName=智多星}
HeroNode{no=1, name=宋江, nickName=及时雨}
5.合并两个有序链表
//合并两个有序单链表
public static void merge(HeroNode head1,HeroNode head2){
HeroNode temp1 = head1.next;
HeroNode temp2 = head2.next;
HeroNode reverse =new HeroNode(0,"","");
//要遍历的节点
HeroNode t =null;
//新节点的临时头节点
HeroNode p = reverse;
while (temp1 != null || temp2 != null) {
//循环到第一个为空时,只遍历第二个链表
if(temp1 ==null){
reverse.next=temp2;
temp2= temp2.next;
reverse=reverse.next;
//循环到第二个为空时,只遍历第一个链表
}else if(temp2 ==null){
reverse.next=temp1;
temp1= temp1.next;
reverse=reverse.next;
}else if(temp1.no<temp2.no){
t = temp1;
temp1=temp1.next;
reverse.next=t;
reverse=reverse.next;
}else{
t = temp2;
temp2=temp2.next;
reverse.next=t;
reverse=reverse.next;
}
}
head1.next = p.next;
}
System.out.println("--------合并打印链表---------");
merge(singleLinkedList.getHead(),singleLinkedList2.getHead());
singleLinkedList.list();
--------链表1---------
HeroNode{no=1, name=宋江, nickName=及时雨}
HeroNode{no=4, name=林冲, nickName=豹子头}
HeroNode{no=7, name=卢俊义7, nickName=玉麒麟}
HeroNode{no=8, name=吴用8, nickName=智多星}
HeroNode{no=10, name=李逵10, nickName=黑旋风}
--------链表2---------
HeroNode{no=2, name=卢俊义, nickName=玉麒麟}
HeroNode{no=3, name=吴用, nickName=智多星}
HeroNode{no=5, name=李逵, nickName=黑旋风}
HeroNode{no=6, name=宋江6, nickName=及时雨}
HeroNode{no=9, name=林冲9, nickName=豹子头}
--------合并打印链表---------
HeroNode{no=1, name=宋江, nickName=及时雨}
HeroNode{no=2, name=卢俊义, nickName=玉麒麟}
HeroNode{no=3, name=吴用, nickName=智多星}
HeroNode{no=4, name=林冲, nickName=豹子头}
HeroNode{no=5, name=李逵, nickName=黑旋风}
HeroNode{no=6, name=宋江6, nickName=及时雨}
HeroNode{no=7, name=卢俊义7, nickName=玉麒麟}
HeroNode{no=8, name=吴用8, nickName=智多星}
HeroNode{no=9, name=林冲9, nickName=豹子头}
HeroNode{no=10, name=李逵10, nickName=黑旋风}
四.双链表
1.单链表的缺点
1.查找的方向只能是一个方向。双向链表可以向前或者向后查找。
2.单向链表不能自我删除,需要靠辅助节点。而双向链表,可以自我删除
2.添加
1.遍历方法和单链表一样,只是可以向前,也可以向后查找
2.添加(默认添加到双向链表的最后)
temp.next = new Node();
new Node.pre = temp;
package com.hyy.linkedList;
/**
* @Author:
* @Description:
* @Date:2021/1/12
* @Modified By:
*/
public class DoubleLinkedListDemo {
public static void main(String[] args) {
DoubleLinkedList linkedList = new DoubleLinkedList();
HeroNode2 hero1 =new HeroNode2(1,"宋江","及时雨");
HeroNode2 hero2 =new HeroNode2(2,"卢俊义","玉麒麟");
HeroNode2 hero3 =new HeroNode2(3,"吴用","智多星");
HeroNode2 hero4 =new HeroNode2(4,"林冲","豹子头");
HeroNode2 hero5 =new HeroNode2(5,"李逵","黑旋风");
linkedList.add(hero1);
linkedList.add(hero3);
linkedList.add(hero4);
linkedList.add(hero5);
linkedList.add(hero2);
linkedList.list();
}
}
class DoubleLinkedList{
//先初始化一个头节点,头节点不要动
private HeroNode2 head=new HeroNode2(0,"","");
public HeroNode2 getHead() {
return head;
}
//遍历双向链表的方法
public void list(){
if (head.next == null) {
return;
}
HeroNode2 temp = head.next;
while (true){
if (temp==null){
break;
}
System.out.println(temp.toString());
temp = temp.next;
}
}
//添加
public void add(HeroNode2 node){
HeroNode2 temp = head;
while (temp.next!=null){
temp=temp.next;
}
//形成双向链表
temp.next=node;
node.pre=temp;
}
}
/*
* 定义一个HeroNode,每个HeroNode对象就是一个头节点
*/
class HeroNode2{
public int no;
public String name;
public String nickName;
public HeroNode2 next;
public HeroNode2 pre;
//构造器
public HeroNode2(int hNo,String name,String nickName){
this.no = hNo;
this.name = name;
this.nickName = nickName;
}
@Override
public String toString() {
return "HeroNode{" +
"no=" + no +
", name=" + name +
", nickName=" + nickName +
'}';
}
}
----------------------------
HeroNode{no=1, name=宋江, nickName=及时雨}
HeroNode{no=3, name=吴用, nickName=智多星}
HeroNode{no=4, name=林冲, nickName=豹子头}
HeroNode{no=5, name=李逵, nickName=黑旋风}
HeroNode{no=2, name=卢俊义, nickName=玉麒麟}
3.删除
双向链表,可以实现自我删除
temp.pre.next = temp.next;
temp.next.pre = temp.pre;
//删除
public void delete(int no){
HeroNode2 temp = head.next;
boolean flag = false;
while (true) {
if (temp== null) {
break;
}
if(temp.no==no){
flag =true;
break;
}
temp=temp.next;
}
if (flag) {
//删除的节点不是最后一个节点
if(temp.next!=null){
temp.next.pre=temp.pre;
}
temp.pre.next=temp.next;
}else {
System.out.printf("不存在编号为%d的英雄\n",no);
}
}
//删除
linkedList.delete(4);
linkedList.list();
HeroNode{no=1, name=宋江, nickName=及时雨}
HeroNode{no=3, name=吴用, nickName=智多星}
HeroNode{no=4, name=林冲, nickName=豹子头}
HeroNode{no=5, name=李逵, nickName=黑旋风}
HeroNode{no=2, name=卢俊义, nickName=玉麒麟}
--------删除后的链表---------
HeroNode{no=1, name=宋江, nickName=及时雨}
HeroNode{no=3, name=吴用, nickName=智多星}
HeroNode{no=5, name=李逵, nickName=黑旋风}
HeroNode{no=2, name=卢俊义, nickName=玉麒麟}
4.双向链表按顺序添加
//按顺序添加
public void addByOrder(HeroNode2 node){
HeroNode2 temp = head;
boolean flag=false;
while (true){
if(temp.next==null){
break;
}
if(temp.no>node.no){
break;
}
if (temp.no==node.no){
flag=true;
break;
}
temp=temp.next;
}
if (flag) {
System.out.printf("编号%d存在,不能插入\n",node.no);
return;
}else {
if(temp.next==null){
temp.next=node;
node.pre=temp;
return;
}
//顺序添加4条线
temp.pre.next=node;
node.pre=temp.pre;
temp.pre=node;
node.next=temp;
}
}
linkedList.addByOrder(hero1);
linkedList.addByOrder(hero3);
linkedList.addByOrder(hero4);
linkedList.addByOrder(hero5);
linkedList.addByOrder(hero2);
linkedList.list();
------------------
HeroNode{no=1, name=宋江, nickName=及时雨}
HeroNode{no=2, name=卢俊义, nickName=玉麒麟}
HeroNode{no=3, name=吴用, nickName=智多星}
HeroNode{no=4, name=林冲, nickName=豹子头}
HeroNode{no=5, name=李逵, nickName=黑旋风}
五.单向环形链表
1.环形链表生成
public class Joseph {
public static void main(String[] args) {
CircleLinkedList joseph = new CircleLinkedList();
joseph.add(7);
joseph.list();
}
}
class CircleLinkedList{
//创建一个first节点
private Boy first =null;
//添加小孩节点,构建环形链表
public void add(int nums){
if(nums<1){
System.out.println("nums的值不正确");
return;
}
Boy temp =null;
for (int i = 1; i <= nums; i++) {
//根据编号创建节点
Boy b=new Boy(i);
if(i==1){
first=b;
b.setNext(first);//构成环
temp=first;
}else{
temp.setNext(b);
b.setNext(first);
temp=temp.getNext();
}
}
}
public void list(){
if(first==null){
System.out.println("-----空环形链表------");
return;
}
Boy temp = first;
while (true){
System.out.printf("小孩的编号为:%d\n",temp.getNo());
if(temp.getNext()==first){
break;
}
temp = temp.getNext();
}
}
}
//创建一个Boy类,表示一个节点
class Boy{
private int no;
private Boy next;//指向下一个节点
public Boy(int no){
this.no = no;
}
public Boy getNext() {
return next;
}
public void setNext(Boy next) {
this.next = next;
}
public int getNo() {
return no;
}
public void setNo(int no) {
this.no = no;
}
}
-------------------
小孩的编号为:1
小孩的编号为:2
小孩的编号为:3
小孩的编号为:4
小孩的编号为:5
小孩的编号为:6
小孩的编号为:7
约瑟夫问题:
1.创建一个辅助指针(变量)temp,事先指向最后一个节点
2.当小孩报数时,让temp指针移动m-1次。
public void joseph(int k,int m){
//辅助节点放在最后一个节点上
Boy temp = first;
while (true){
if(temp.getNext()==first){
break;
}
temp=temp.getNext();
}
//移动k-次(第几个小孩报数)
for (int i = 1; i < k; i++) {
temp=temp.getNext();
}
while (true){
if(first.getNext()==first){
System.out.printf("小孩的编号为:%d\n",first.getNo());
break;
}
for (int i = 1; i < m; i++) {
temp = temp.getNext();
}
System.out.printf("小孩的编号为:%d\n",temp.getNext().getNo());
temp.setNext(temp.getNext().getNext());
//移动first
first=temp;
}
}
-------------
小孩的编号为:1
小孩的编号为:2
小孩的编号为:3
小孩的编号为:4
小孩的编号为:5
----------约瑟夫---------
小孩的编号为:2
小孩的编号为:4
小孩的编号为:1
小孩的编号为:5
小孩的编号为:3