Java数据结构-链表(LinkedList)(四)

for (int i = 0; i <= index - 1 ; i++) {

pre = pre.next;

}

//要找到index位置的结点

Node curr = pre.next;

pre.next = new Node(t,curr);

this.N ++;

}

/**

  • 删除并返回链表中第i个数据元素

  • @param index

  • @return

*/

public T remove(int index){

//找到index位置前一个
结点

Node pre = this.head;

for (int i = 0; i < index ; i++) {

pre = pre.next;

}

//要找到index位置的结点

Node curr = pre.next;

//找到index位置的下一个结点

Node nextNode = curr.next;

pre.next = nextNode;

this.N --;

return curr.item;

}

/**

*返回链表中首次出现的指定的数据元素的位序号,若不存在,则返回-1

  • @param t

  • @return

*/

public int indexOf(T t){

Node pre = this.head;

int count = 0;

while (pre.next!=null){

count ++ ;

pre = pre.next;

if(pre.item.equals(t)){

return count;

}

}

return -1;

}

@Override

public Iterator iterator() {

return new MyIterator();

}

private class MyIterator implements Iterator{

private Node n;

public MyIterator(){

this.n=head;

}

@Override

public boolean hasNext() {

return n.next!=null;

}

@Override

public Object next() {

n = n.next;

return n.item;

}

}

}

单向链表的案例

  • 单链表的反转【腾讯面试题】

// 单链表的反转【腾讯面试题】

// 方式一、 递归方式翻转

/**

  • 用来反转整个链表

*/

public void reverse1(){

//判断当前链表是否为空链表,如果是空链表,则结束运行,如果不是,则调用重载的reverse方法完成反转

if (isEmpty()){

return;

}

reverse(head.next);

}

/**

  • 反转指定的结点curr,并把反转后的结点返回

  • @param curr

  • @return

*/

public Node reverse(Node curr){ // aa bb cc

if (curr.next==null){

head.next=curr;

return curr;

}

//递归的反转当前结点curr的下一个结点;返回值就是链表反转后,当前结点的上一个结点

Node pre = reverse(curr.next);

//让返回的结点的下一个结点变为当前结点curr;

pre.next=curr;

//把当前结点的下一个结点变为null

curr.next=null;

return curr;

}

方式二

  • 从尾到头打印单链表 【百度面试题】

方式 1:反向遍历(这里就不写代码了,和前一个基本一样)

方式 2:Stack 栈 (利用先进后出的特点,来实现逆序打印效果)

2. 单向循环链表

单链表的一个变形是单向循环链表,链表中最后一个节点的next域不再为null,而是指向链表的头节点。

单线无头循环链表代码实现

public class MyHeadlessLinkedList {

// 虽然没有头部但是依旧需要使用一个字段来记录第一个值

private Node head;

//记录链表的长度

private int N;

public MyHeadlessLinkedList(){

}

/**

  • 定义节点类

*/

public class Node{

T item ; //存储数据

Node next; //下一个节点

public Node(T item, Node next) {

this.item = item;

this.next = next;

}

}

/**

  • 清空链表

*/

public void clear(){

this.head = null;

this.N = 0;

}

/**

  • 判断链表是否为空,是返回true,否返回false

  • @return

*/

public boolean isEmpty(){

return this.N == 0;

}

/**

  • 获取链表中元素的个数

  • @return

*/

public int length(){

return this.N;

}

/**

  • 读取并返回链表中的第index个元素的值

  • @param index

  • @return

*/

public T get(int index){

if(index > this.N){

System.out.println(“链表长度越界!”);

return null;

}

Node n = this.head;

for (int i = 0; i < index; i++) {

n = n.next;

}

return n.item;

}

public Node getNode(int index){

if(index > this.N){

System.out.println(“链表长度越界!”);

return null;

}

Node n = this.head;

for (int i = 0; i < index; i++) {

n = n.next;

}

return n;

}

/**

  • 往链表中添加一个元素

  • @param t

*/

public void insert(T t){

if(isEmpty()){

this.head = new Node(t, null);

}

Node n = this.head;

for (int i = 1; i < this.N ; i++) {

n=n.next;

}

//让当前最后一个结点指向新结点

n.next = new Node(t, this.head);

N ++;

}

/**

  • 在链表的第index个元素之前插入一个值为t的数据元素

  • @param index

  • @param t

*/

public void insert(int index,T t){

//找到index位置前一个结点

Node pre = this.head;

for (int i = 0; i <= index - 1 ; i++) {

pre = pre.next;

}

//要找到index位置的结点

Node curr = pre.next;

pre.next = new Node(t,curr);

this.N ++;

}

/**

  • 删除并返回链表中第i个数据元素

  • @param index

  • @return

*/

public T remove(int index){

//找到index位置前一个结点

Node curr = null;

if(index == 0){ // index 为0时表示需要重新处理head

curr = head;

head = head.next;

}else {

Node pre = this.head;

for (int i = 0; i < index - 1 ; i++) {

pre = pre.next;

}

//要找到index位置的结点

curr = pre.next;

//找到index位置的下一个结点

pre.next = curr.next;

}

this.N --;

return curr.item;

}

/**

*返回链表中首次出现的指定的数据元素的位序号,若不存在,则返回-1

  • @param t

  • @return

*/

public int indexOf(T t){

Node pre = this.head;

int count = 0;

while (pre.next!=null){

count ++ ;

pre = pre.next;

if(pre.item.equals(t)){

return count;

}

}

return -1;

}

}

案例使用 约瑟夫问题

问题描述:

传说有这样一个故事,在罗马人占领乔塔帕特后,39 个犹太人与约瑟夫及他的朋友躲到一个洞中,39个犹太人决定宁愿死也不要被敌人抓到,于是决定了一个自杀方式,41个人排成一个圆圈,第一个人从1开始报数,依次往后,如果有人报数到3,那么这个人就必须自杀,然后再由他的下一个人重新从1开始报数,直到所有人都自杀身亡为止。然而约瑟夫和他的朋友并不想遵从。于是,约瑟夫要他的朋友先假装遵从,他将朋友与自己安排在第16个与第31个位置,从而逃过了这场死亡游戏。

问题转换:

  1. 41个人坐一圈,第一个人编号为1,第二个人编号为2,第n个人编号为n。

  2. 编号为1的人开始从1报数,依次向后,报数为3的那个人退出圈;

  3. 自退出那个人开始的下一个人再次从1开始报数,以此类推;

  4. 求出最后退出的那个人的编号。

解题思路:

  1. 构建含有41个结点的单向循环链表,分别存储1~41的值,分别代表这41个人;

  2. 使用计数器count,记录当前报数的值;

  3. 遍历链表,每循环一次,count++;

  4. 判断count的值,如果是3,则从链表中删除这个结点并打印结点的值,把count重置为0

代码实现

  • 方式一(链表实现)、

public static void main(String[] args) {

/*

  • "约瑟夫环"问题

  • 共S个人,从第F个人开始报数,报数1—N

  • 这里初始化的41个,人从第1个开始报数,数到3的出局

  • S 为人数 ,F 为开始人的编号 ,N为需要走的个数

  • 最后运行结果:3,6,9,12,15,18,21,24,27,30,33,36,39,1,5,10,14,19,23,28,32,37,41,7,13,20,26,34,40,8,17,29,38,11,25,2,22,4,35,16,31

*/

int S=41;

int F=1;

int N=3;

MyHeadlessLinkedList iml = new MyHeadlessLinkedList();

for (int i = 1; i <= S; i++) {

iml.insert(i);

}

MyHeadlessLinkedList.Node node = iml.getNode(F-1); // 因为下标零开始所以 F-1

MyHeadlessLinkedList.Node temp=null;

int count = 0 ;

while (node.next != node){ //当自己与自己的next相等时,表示剩最后一个了停止循环

count++ ;

if(count == N ){

temp.next = node.next;

System.out.print(node.item+“,”);

count = 0;

}else {

temp = node;

}

node = node.next;

}

System.out.println(node.item+“\n”);

}

方式二

public static void main(String args[]) {

final int N=41,S=1,M=3;

int i = S-1, k = N, g = 1, j;

int a[] = new int[N]; //使用数组存放人;

for(int h = 1; h <= N; h++){

a[h-1] = h; //将编号为h的人存到下标为h-1的数组中

}

do {

i = i + (M -1) ; //计算出圈人下标,因为自身需要报数所以减一

while(i >= k)

i = i-k;

System.out.print(a[i]+“,”);

j = i;

while ( j < k-1) {

a[j] = a[j+1]; //第i个人出圈后将后面的人的编号往前移动,相当于把数到5的踢出局

j++;

}

k–; //圈中人数k-1

g++; //循环次数g+1

}while(g <= N); //最多N轮循环

}

方式三

public static void main(String args[]) {

final int N = 41, S = 1, M = 3; //N为总人数,从第S个人开始报数,报数到M的为出圈

int a[] = new int[N + 1];

int i, j, k;

k = S - 1; //k等于S-1,表示从S开始数出圈人的下标

for (i = 1; i <= N; i++) {

for (j = 1; j <= M; j++) {

if (k == N)

k = 1; //当出圈人的下标到末尾时,记数从1开始

else

k++;

if (a[k] == 1) //a[k] 做标记,说明下标为k的人已经出圈了

j–; //j-1, 以保证每次数超过M个

}

a[k] = 1; //标记出圈

System.out.print((k) + “,”); //下标为k的人标号为k

}

}

3. 双向链表

一种更复杂的链表是**“双向链表”“双面链表”**。每个节点有两个链接:一个指向前一个节点,当此节点为第一个节点时,指向空值;而另一个指向下一个节点,当此节点为最后一个节点时,指向空值。

直接上代码

public class TowWayLinkedList implements Iterable{

// 首节点

private Node head;

// 尾节点

private Node last;

// 链表长度

private int N;

public TowWayLinkedList(){

this.head = new Node(null,null,null);

this.last = null;

this.N = 0;

}

class Node {

T item;

Node next;

Node pre;

public Node(T item,Node next,Node pre){

this.item = item;

this.next = next;

this.pre = pre;

}

}

/**

  • 空置链表

*/

public void clear(){

this.last = null;

this.head = new Node(null,null,null);

this.N = 0;

}

/**

  • 判断链表是否为空,是返回true,否返回false

  • @return

*/

public boolean isEmpty(){

return this.N==0;

}

/**

  • 获取链表中元素的个数;

  • @return

*/

public int length(){

return this.N;

}

/**

  • :读取并返回链表中的第i个元素的值

  • @param indix

  • @return

*/

public T get(int indix){

Node node = this.head;

for (int i = 0; i < indix; i++) {

node = node.next;

}

return node.item;

}

/**

  • 往链表中添加一个元素

  • @param t

*/

public void insert(T t){

if(this.last==null){

last = new Node(t,null,head);

head.next = last;

}else{

Node oldLast = this.last;

Node node = new Node(t,null,oldLast);

oldLast.next = node;

last = node;

}

N++;

}

/**

  • 在链表的第i个元素之前插入一个值为t的数据元素

  • @param index

  • @param t

*/

public void insert(int index,T t){

Node node = this.head;

// 获取需要插入数据的上一个节点

for (int i = 0; i < index; i++) {

node = node.next;

}

// 当前节点

Node curr = node.next;

// 需要插入的节点,上一个节点为原来的上一个节点,下一个节点是当前节点的位置

Node newNode = new Node(t, curr, node);

// 上一个节点的next标记为新增节点

node.next = newNode;

// 当前节点往后移以为,上一个节点标记为新增节点

curr.pre = newNode;

N++;

}

/**

*删除并返回链表中第i个数据元素;

  • @param index

  • @return

*/

public T remove(int index){

Node node = this.head;

// 获取需要插入数据的上一个节点

for (int i = 0; i < index; i++) {

node = node.next;

}

// index当前节点

Node curr = node.next;

// index 下一个节点

Node currNext = curr.next;

// 上一个节点的next标记为index的下一个节点

node.next = currNext;

// index 下一个节点的pre标记为index的上一个节点

currNext.pre = node;

N–;

return curr.item;

}

/**

  • 返回链表中首次出现的指定的数据元素的位序号,若不存在,则返回-1

  • @param t

  • @return

*/

public int indexOf(T t){

Node node = this.head;

// 获取需要插入数据的上一个节点

int count = 0;

while (node.next != null) {

node = node.next;

if(node.item.equals(t)){

return count;

}

count ++;

}

return -1;

}

/**

  • 获取第一个元素

  • @return

*/

public T getFirst(){

if(isEmpty()){

return null;

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值