【算法数据结构初阶篇】:链表问题

news2024/11/27 16:34:04

一、反转单双链表

一、数据结构图

二、代码演示

public class Code01_ReverseList {

	public static class Node {
		public int value;
		public Node next;

		public Node(int data) {
			value = data;
		}
	}

	public static class DoubleNode {
		public int value;
		public DoubleNode last;
		public DoubleNode next;

		public DoubleNode(int data) {
			value = data;
		}
	}

	public static Node reverseLinkedList(Node head) {
		Node pre = null;
		Node next = null;
		while (head != null) {
			next = head.next;
			head.next = pre;
			pre = head;
			head = next;
		}
		return pre;
	}

	public static DoubleNode reverseDoubleList(DoubleNode head) {
		DoubleNode pre = null;
		DoubleNode next = null;
		while (head != null) {
			next = head.next;
			head.next = pre;
			head.last = next;
			pre = head;
			head = next;
		}
		return pre;
	}

二、单链表实现队列和栈

特点:

队列:先进先出

栈:   先进后出

一、代码演示

package class04;

import java.util.LinkedList;
import java.util.Queue;
import java.util.Stack;

public class Code02_LinkedListToQueueAndStack {

	public static class Node<V> {
		public V value;
		public Node<V> next;

		public Node(V v) {
			value = v;
			next = null;
		}
	}

	public static class MyQueue<V> {
		private Node<V> head;
		private Node<V> tail;
		private int size;

		public MyQueue() {
			head = null;
			tail = null;
			size = 0;
		}

		public boolean isEmpty() {
			return size == 0;
		}

		public int size() {
			return size;
		}

		public void offer(V value) {
			Node<V> cur = new Node<V>(value);
			if (tail == null) {
				head = cur;
				tail = cur;
			} else {
				tail.next = cur;
				tail = cur;
			}
			size++;
		}

		// C/C++的同学需要做节点析构的工作
		public V poll() {
			V ans = null;
			if (head != null) {
				ans = head.value;
				head = head.next;
				size--;
			}
			if (head == null) {
				tail = null;
			}
			return ans;
		}

		// C/C++的同学需要做节点析构的工作
		public V peek() {
			V ans = null;
			if (head != null) {
				ans = head.value;
			}
			return ans;
		}

	}

	public static class MyStack<V> {
		private Node<V> head;
		private int size;

		public MyStack() {
			head = null;
			size = 0;
		}

		public boolean isEmpty() {
			return size == 0;
		}

		public int size() {
			return size;
		}

		public void push(V value) {
			Node<V> cur = new Node<>(value);
			if (head == null) {
				head = cur;
			} else {
				cur.next = head;
				head = cur;
			}
			size++;
		}

		public V pop() {
			V ans = null;
			if (head != null) {
				ans = head.value;
				head = head.next;
				size--;
			}
			return ans;
		}

		public V peek() {
			return head != null ? head.value : null;
		}

	}

	public static void testQueue() {
		MyQueue<Integer> myQueue = new MyQueue<>();
		Queue<Integer> test = new LinkedList<>();
		int testTime = 5000000;
		int maxValue = 200000000;
		System.out.println("测试开始!");
		for (int i = 0; i < testTime; i++) {
			if (myQueue.isEmpty() != test.isEmpty()) {
				System.out.println("Oops!");
			}
			if (myQueue.size() != test.size()) {
				System.out.println("Oops!");
			}
			double decide = Math.random();
			if (decide < 0.33) {
				int num = (int) (Math.random() * maxValue);
				myQueue.offer(num);
				test.offer(num);
			} else if (decide < 0.66) {
				if (!myQueue.isEmpty()) {
					int num1 = myQueue.poll();
					int num2 = test.poll();
					if (num1 != num2) {
						System.out.println("Oops!");
					}
				}
			} else {
				if (!myQueue.isEmpty()) {
					int num1 = myQueue.peek();
					int num2 = test.peek();
					if (num1 != num2) {
						System.out.println("Oops!");
					}
				}
			}
		}
		if (myQueue.size() != test.size()) {
			System.out.println("Oops!");
		}
		while (!myQueue.isEmpty()) {
			int num1 = myQueue.poll();
			int num2 = test.poll();
			if (num1 != num2) {
				System.out.println("Oops!");
			}
		}
		System.out.println("测试结束!");
	}

	public static void testStack() {
		MyStack<Integer> myStack = new MyStack<>();
		Stack<Integer> test = new Stack<>();
		int testTime = 5000000;
		int maxValue = 200000000;
		System.out.println("测试开始!");
		for (int i = 0; i < testTime; i++) {
			if (myStack.isEmpty() != test.isEmpty()) {
				System.out.println("Oops!");
			}
			if (myStack.size() != test.size()) {
				System.out.println("Oops!");
			}
			double decide = Math.random();
			if (decide < 0.33) {
				int num = (int) (Math.random() * maxValue);
				myStack.push(num);
				test.push(num);
			} else if (decide < 0.66) {
				if (!myStack.isEmpty()) {
					int num1 = myStack.pop();
					int num2 = test.pop();
					if (num1 != num2) {
						System.out.println("Oops!");
					}
				}
			} else {
				if (!myStack.isEmpty()) {
					int num1 = myStack.peek();
					int num2 = test.peek();
					if (num1 != num2) {
						System.out.println("Oops!");
					}
				}
			}
		}
		if (myStack.size() != test.size()) {
			System.out.println("Oops!");
		}
		while (!myStack.isEmpty()) {
			int num1 = myStack.pop();
			int num2 = test.pop();
			if (num1 != num2) {
				System.out.println("Oops!");
			}
		}
		System.out.println("测试结束!");
	}

	public static void main(String[] args) {
		testQueue();
		testStack();
	}

}

 三、双链表实现双端队列

一、代码演示

package class04;

import java.util.Deque;
import java.util.LinkedList;

public class Code03_DoubleLinkedListToDeque {

	public static class Node<V> {
		public V value;
		public Node<V> last;
		public Node<V> next;

		public Node(V v) {
			value = v;
			last = null;
			next = null;
		}
	}

	public static class MyDeque<V> {
		private Node<V> head;
		private Node<V> tail;
		private int size;

		public MyDeque() {
			head = null;
			tail = null;
			size = 0;
		}

		public boolean isEmpty() {
			return size == 0;
		}

		public int size() {
			return size;
		}

		public void pushHead(V value) {
			Node<V> cur = new Node<>(value);
			if (head == null) {
				head = cur;
				tail = cur;
			} else {
				cur.next = head;
				head.last = cur;
				head = cur;
			}
			size++;
		}

		public void pushTail(V value) {
			Node<V> cur = new Node<>(value);
			if (head == null) {
				head = cur;
				tail = cur;
			} else {
				tail.next = cur;
				cur.last = tail;
				tail = cur;
			}
			size++;
		}

		public V pollHead() {
			V ans = null;
			if (head == null) {
				return ans;
			}
			size--;
			ans = head.value;
			if (head == tail) {
				head = null;
				tail = null;
			} else {
				head = head.next;
				head.last = null;
			}
			return ans;
		}

		public V pollTail() {
			V ans = null;
			if (head == null) {
				return ans;
			}
			size--;
			ans = tail.value;
			if (head == tail) {
				head = null;
				tail = null;
			} else {
				tail = tail.last;
				tail.next = null;
			}
			return ans;
		}

		public V peekHead() {
			V ans = null;
			if (head != null) {
				ans = head.value;
			}
			return ans;
		}

		public V peekTail() {
			V ans = null;
			if (tail != null) {
				ans = tail.value;
			}
			return ans;
		}

	}

	public static void testDeque() {
		MyDeque<Integer> myDeque = new MyDeque<>();
		Deque<Integer> test = new LinkedList<>();
		int testTime = 5000000;
		int maxValue = 200000000;
		System.out.println("测试开始!");
		for (int i = 0; i < testTime; i++) {
			if (myDeque.isEmpty() != test.isEmpty()) {
				System.out.println("Oops!");
			}
			if (myDeque.size() != test.size()) {
				System.out.println("Oops!");
			}
			double decide = Math.random();
			if (decide < 0.33) {
				int num = (int) (Math.random() * maxValue);
				if (Math.random() < 0.5) {
					myDeque.pushHead(num);
					test.addFirst(num);
				} else {
					myDeque.pushTail(num);
					test.addLast(num);
				}
			} else if (decide < 0.66) {
				if (!myDeque.isEmpty()) {
					int num1 = 0;
					int num2 = 0;
					if (Math.random() < 0.5) {
						num1 = myDeque.pollHead();
						num2 = test.pollFirst();
					} else {
						num1 = myDeque.pollTail();
						num2 = test.pollLast();
					}
					if (num1 != num2) {
						System.out.println("Oops!");
					}
				}
			} else {
				if (!myDeque.isEmpty()) {
					int num1 = 0;
					int num2 = 0;
					if (Math.random() < 0.5) {
						num1 = myDeque.peekHead();
						num2 = test.peekFirst();
					} else {
						num1 = myDeque.peekTail();
						num2 = test.peekLast();
					}
					if (num1 != num2) {
						System.out.println("Oops!");
					}
				}
			}
		}
		if (myDeque.size() != test.size()) {
			System.out.println("Oops!");
		}
		while (!myDeque.isEmpty()) {
			int num1 = myDeque.pollHead();
			int num2 = test.pollFirst();
			if (num1 != num2) {
				System.out.println("Oops!");
			}
		}
		System.out.println("测试结束!");
	}

	public static void main(String[] args) {
		testDeque();
	}

}

四、Leetcode:25. K 个一组翻转链表

一、题意描述

二、代码演示 

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode() {}
 *     ListNode(int val) { this.val = val; }
 *     ListNode(int val, ListNode next) { this.val = val; this.next = next; }
 * }
 */
class Solution {
    public ListNode reverseKGroup(ListNode head, int k) {
        //1.进行K个一组的头尾获取
        ListNode start = head;
        ListNode end = getKGroupEnd(start,k);
        //2.此时如果链表大于等于k个元素,end不会为空,否则为空,则无需倒序直接返回原链表头节点
        if(end == null){
            return head;
        }
        //3.走到这里说明有K个元素及以上,所以就可以定义头节点,第一组倒序后,end节点即为头节点,进行保存,最后返回,需先赋值end,再倒序,因为倒序后end则指向了end.next了
        head = end;
        reverse(start,end);
        
        //4.进行下一轮的K个一组的倒序链表遍历操作,需要定义第一组的最后一个节点,倒序后就是start节点
        ListNode lastEnd = start;
        while(lastEnd.next != null){
            //这里重新定义下一组的start,我们在倒序函数中,最后定义了start的指向K+1元素节点,所以就可以直接赋值为lastEnd.next
            start = lastEnd.next;
            end = getKGroupEnd(start,k);
            if(end == null){
                return head;
            }
            reverse(start,end);
            //下一组倒序后,接着就是要跟前一组连接起来。lastEnd时上组最后元素,需要指向下组的首元素,倒序后首元素为end .    然后接着重新指向下一组的最后元素,即start接着往后进行遍历
            lastEnd.next = end;
            lastEnd = start;
        }
        return head;
    }

    //返回K个一组的最后一个元素 
    public ListNode getKGroupEnd(ListNode start, int k){
        while( --k > 0 && start != null){
            start = start.next;
        }
        return start;
    }

    //K个一组倒序
    public void reverse(ListNode start, ListNode end){
    //重点:先把end节点往后移动一个元素,因为倒序时要让程序走到原始的end 把end指向前一个元素。 判断条件就时需要走到
    //end后一个元素 再结束 ,如果走到end结束 end就没有走指针换序逻辑就跳出循环了。
    end = end.next;
    ListNode pre = null;
    ListNode next = null;
    ListNode cur = start;
    while(cur != end){
        next = cur.next;
        cur.next = pre;
        pre = cur;
        cur = next;
    }
    //最后start的指向也要调整,倒序后,start就跑到了原始end,接着就要指向end的下一个元素
    start.next = end;

    }
}

三、核心思路

  • 首先是可以看到需要进行倒序,并且还是一个K区间内的倒序,可以先分别定义好首尾元素,该区间的倒序函数 reverse,以及取到K区间的尾元素函数getKGroupEnd。
  • 接着就是开始第一轮的K区间元素翻转,满足K个及以上,则可以确定翻转,然后要定义好头节点,因为第一轮翻转后,就是能将end元素确定为头节点,最后进行返回,若不到K个,则无需进行翻转,直接return end
  • 如果有K个以上,说明就要进行遍历,这里依次遍历,要注意的是 上组尾元素laststart的next指向是下组首元素end,然后上组尾元素要往下组移动,移动到下组尾元素start,翻转后start就是尾元素,end就是首元素。

 

五、Leetcode: 2. 两数相加

 一、题意描述

二、代码演示

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode() {}
 *     ListNode(int val) { this.val = val; }
 *     ListNode(int val, ListNode next) { this.val = val; this.next = next; }
 * }
 */
class Solution {
    public ListNode addTwoNumbers(ListNode l1, ListNode l2) {
		int len1 = listLength(l1);
		int len2 = listLength(l2);
		//1.求出长链表和短链表赋值l  s 
		ListNode l = len1 >= len2 ? l1 : l2;
		ListNode s = l == l1 ? l2 : l1;
		//2.赋值临时变量进行长短链表遍历,因为我们把加后的结果都是覆盖到了长链表,所以最后return时要返回l 不能直接对l遍历指向
		ListNode curL = l;
		ListNode curS = s;
		//3.这个last变量是用来表示最后遍历到最后一位时,curL指针指向null跳出如果存在前一位累加有进位,那就需要再接着指向一个新节点,值为1
		//那么每次遍历都保持当前的curL,最后跳出时仍保留最后一个元素 所以可以进行next指向
		ListNode last = curL;
		//4.两数相加是否进位,初始值0,如5+5=10就进位,carry赋值1 如3=5=8没进位,carry=0
		int carry = 0;
		//5.记录当前位的值
		int curNum = 0;
		//6遍历阶段1:当短链表和长链表都有数时
		while (curS != null) {
			//当前值就等于两数和加进位,第一轮进位0 
			curNum = curL.val + curS.val + carry;
			//模10得出当前位得值,直接覆盖长链表节点得值
			curL.val = (curNum % 10);
			//除以10看是否两数和累加有进位,有则1 无则0
			carry = curNum / 10;
			//保存当前链表节点。避免退出循环后,curL指向空了。 这里只有当两链表等长 curL才会为空 因为等长就跟cur遍历到最后一位
			//然后最后接着又重新赋值指向next,此时就为空,那么提前保存了curL的last就不为空了
			last = curL;
			curL = curL.next;
			curS = curS.next;
		}
		//遍历阶段2:短链表遍历完,接着剩下长链表单独遍历
		while (curL != null) {
			curNum = curL.val + carry;
			curL.val = (curNum % 10);
			carry = curNum / 10;
			last = curL;
			curL = curL.next;
		}
		//遍历阶段3:长链表也遍历完,前面跳出循环后last保存了curL的尾元素,如果前面遍历完后最后一位累计和大于等于10 则进位就是1,
		//那么就需要在last节点后面在指向一个值为1的进位元素 
		if (carry != 0) {
			last.next = new ListNode(1);
		}
		//7.最后返回结果都覆盖到了长链表 返回最先临时保存长链表头节点的变量l
		return l;
	}

	// 求链表长度
	public static int listLength(ListNode head) {
		int len = 0;
		while (head != null) {
			len++;
			head = head.next;
		}
		return len;
	}
}

 三、核心思路

可以先定性确定两链表谁长谁短,然后往下开始按位遍历累加,这里因为存在两数相加大于等于10,则会需要进位+1,所以要设置一个进位数,初始值0,进位变量可以通过除以10,进行判断,只有两种结果0或1 ,进行进位累加

然后遍历的时候需要分三种情况:

遍历阶段1:当短链表和长链表都有数时

遍历阶段2:短链表遍历完,接着剩下长链表单独遍历

遍历阶段3:长链表也遍历完,前面跳出循环后last保存了curL的尾元素,如果前面遍历完后最后一位累计和大于等于10 则进位就是1,那么就需要在last节点后面在指向一个值为1的进位元素

 

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/166838.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Spring Cloud Gateway从注册中心自动注册配置路由信息

环境信息Spring Boot&#xff1a;2.0.8.RELEASESpring Boot内置的tomcat&#xff1a;tomcat-embed-core 8.5.37Spring Cloud Gateway&#xff1a;2.0.4.RELEASENacos&#xff1a;2.0.4.RELEASE需求Spring Cloud Gateway注册到注册中心&#xff08;这里是Nacos&#xff0c;其它注…

Spring学习系列(二)

Spring_特殊值的注入问题和各种类型的自动装配1.set两种方式的区别第4中赋值方式&#xff1a;自动装配&#xff08;只适用于ref类型&#xff09;使用注解定义bean1.set两种方式的区别 &#xff08;1&#xff09;把值写到value属性里面&#xff0c;必须加引号&#xff0c;写到子…

【学习】计算机系统硬件和数据结构

学习内容描述&#xff1a; 1、CPU包含哪些部分&#xff1f; 2、数据结构基础知识。 重点知识&#xff1a; 1、CPU(中央处理器&#xff0c;Central Processing Unit) 主要包括运算器、控制器两大部件&#xff1b;内部结构包含控制单元、运算单元、存储单元和时钟等几个主要部…

虚拟直播(虚拟场景直播)要怎么做?

阿酷TONY / 2022-11-21 / 长沙 绿幕抠像 虚拟场景&#xff08;三维场景&#xff09;实时渲染&#xff0c;来一场虚拟直播。 附案例效果&#xff1a;PC端案例、移动端案例效果。 目录 1. 绿幕虚拟直播间 2. 虚拟场景(炫酷舞台) 3. 案例&#xff1a;PC端 4. 案例&#xff1a…

光纤内窥镜物镜光学设计

光纤内窥镜物镜光学设计 工作原理 典型的光纤传像束内窥镜主要由前置物镜、光纤传像束、目镜/耦接镜、 探测器等组成&#xff0c;如图1所示。通过物镜把目标成像于光纤传像束的前端面上&#xff0c;该端面上的图像被离散分布的大量光纤采样&#xff0c;每根光纤都有良好的光学…

[极客大挑战 2019]Upload

目录 解题步骤 常见的绕过思路 解题步骤 直接上传shell 回显&#xff1a;Not image! bp抓包 修改类型 Content-Type: application/octet-stream改为Content-Type: image/jpg 回显&#xff1a;NOT&#xff01;php! 修改后缀为phtml 回显&#xff1a;NO! HACKER! your file inc…

SAP MM 物料分类账的启用配置

一、前言 物料账就是一本账&#xff0c;管理物料的数量和价值。 通常物料有两种计价方法&#xff1a; 移动平均价V&#xff08;移动加权平均&#xff09; 标准价S 移动平均价的优点&#xff1a;能够及时响应市场原材料价格的波动。 价差科目&#xff08;总账科目&#xff09;…

MyBatis-Plus基本操作

依赖 <dependency><groupId>com.baomidou</groupId><artifactId>mybatis-plus-boot-starter</artifactId><version>3.4.3</version> </dependency>基础操作 DAO层 public interface UserMapper extends BaseMapper<User…

nohup + 命令实现后台不挂断地运行程序

nohup&#xff1a; nohup&#xff1a;不挂断地运行程序&#xff0c;就是即便关闭终端的窗口&#xff0c;程序还会继续执行。 $ nohup python merge_reasons.py可以看到终端仍在被占用&#xff0c;同级目录下生成了一个nohup.out文件&#xff0c;本来输出到终端的信息&#xf…

python人工智能数据算法(下)

文章目录差分法逼近微分背景引入差分法简介差分的不同形式及其代码实现蒙特卡罗方法背景引入蒙特卡罗方法原理蒙特卡罗方法应用计算圆周率计算定积分梯度下降算法算法简介方向导数与梯度梯度下降基于梯度下降算法的线性回归算法总结差分法逼近微分 背景引入 几乎所有的机器学…

我收集的PDF电子书

刚出来&#xff0c;要整理一下自己的资源了&#xff0c;好多都没有了&#xff0c;需要下载的自行联系niunanniunan.net 目录 软件工程 构建之法&#xff08;第1版&#xff09; 实现领域驱动设计 领域驱动设计&#xff1a;软件核心复杂性应对之道 人月神话 算法 算法基础…

C语言文件操作(一)

我们之前写程序&#xff0c;得到运行结果&#xff0c;退出运行&#xff0c;运行结果就不见了&#xff0c;因为运行的结果放到了内存中&#xff0c;退出程序的时候数据就会消失&#xff0c;等下一次运行又要重新输入数据&#xff0c;这样就显得很麻烦。那么我们如何保存之前的运…

Opengl ES之RGB转NV21

前言 在上一篇理论文章中我们介绍了YUV到RGB之间转换的几种公式与一些优化算法&#xff0c;今天我们再来介绍一下RGB到YUV的转换&#xff0c;顺便使用Opengl ES做个实践&#xff0c;将一张RGB的图片通过Shader 的方式转换YUV格式图&#xff0c;然后保存到本地。 可能有的童鞋…

SSL协议未开启是什么意思?

SSL协议未开启是指服务器中的服务没有开启或者没有SSL模块。在互联网中&#xff0c;数据交互传输基于http明文协议&#xff0c;随着互联网的不断发展&#xff0c;http协议展现出它的缺陷&#xff0c;通过http协议传输的数据容易被攻击者窃取、篡改或仿冒。为适应新形势下的网络…

【C语言进阶】文件操作详解

文章目录一.文件指针1.什么是文件指针2.如何使用文件指针二.文件操作函数1.流与文件2.顺序读写函数三.文本文件和二进制文件四.文件缓冲区一.文件指针 1.什么是文件指针 文件指针其实是文件类型指针的简称&#xff0c;我们常常会使用文件保存数据&#xff0c;而每个被使用的文…

SMILEtrack:基于相似度学习的多目标跟踪

文章目录摘要1、简介2、相关工作2.1、Tracking-by-Detection2.1.1、检测方法2.1.2、数据关联方法2.2、Tracking-by-Attention3、方法3.1、体系结构概述3.2.1、图像切片注意(ISA)块3.2.3、Q-K-V注意力块3.3、基于相似匹配级联(SMC)的目标跟踪算法4、实验结果4.1、数据集4.2、MOT…

XMind导入Markdown(利用Typora导出opml)

安装Xmind XMind 是一款非常实用的商业思维导图软件 首先&#xff0c;安装Xmind并打开。通过"帮助"——>“关于Xmind”&#xff0c;可以获取到当前的版本号为 XMind 8 Update 9 在"文件"——>“导入”&#xff0c;可以看到Xmind支持的导入格式仅有…

get请求和post请求

get请求 1.get请求的特点 通过一个URL来访问一个地址&#xff0c;就比如说127.0.0.1:7001,这种请求方式就是get请求&#xff0c;get请求可以直接在URL中添加参数&#xff0c;通过URL来传递参数。 优点&#xff1a;使用简单&#xff0c;清晰有条理&#xff0c;比较适合网站和…

十五天学会Autodesk Inventor,看完这一系列就够了(三),拉伸命令

众所周知&#xff0c;Autocad是一款用于二维绘图、详细绘制、设计文档和基本三维设计&#xff0c;现已经成为国际上广为流行的绘图工具。Autodesk Inventor软件也是美国AutoDesk公司推出的三维可视化实体模拟软件。因为很多人都熟悉Autocad&#xff0c;所以再学习Inventor&…

盘点2022年度A站UE神作top

A站大家都应该很熟悉了&#xff0c;在全球的CG行业都是属于专业化十分高的网站&#xff0c;平台内展示的内容包括影视、动画、娱乐等等板块&#xff0c;更是收录了众多大神艺术家的作品&#xff0c;多看看可以最直接的了解整个行业的审美趋势与技术动向。正好最近2022年A站人气…