栈
栈(Stack)是一种先进后出的数据结构。栈对于数据的操作只能从栈顶进行操作,新放入的数据都位于栈顶,也就是第一的位置,以前放入的就会被向下压,所以数据入栈也叫做压栈。
出栈的形式与入栈类似,每次都只能获取栈顶的元素。如我们要取得“200”就必须先取出“300”,然后再取一次才能成功取得“200”.
public class Test01_Stack {
public static void main(String[] args) {
String[] data = {"a","b","c","d","e"};
Stack<String> stack = new Stack<String>();
for(String s:data){
stack.push(s);// 入栈的方法
}
for(int i=0;i<data.length;i++){
String s = stack.pop();// 出栈的方法
System.out.println(s);// e d c b a
}// Stack类还有其他三个常用操作方法,可以通过查询API了解其使用方法
}
}
- 使用栈的先进后出特性实现快速排序的非递归算法
public class Test03_quickSortNoRec {
public static void main(String[] args) {
int[] a = new int[20];
Random random = new Random();
for(int i=0;i<a.length;i++){
a[i] = random.nextInt(101);
}// 产生随机数组
System.out.println(Arrays.toString(a));
quickSort(a);// 排序
System.out.println(Arrays.toString(a));
}
private static void quickSort(int[] a) {
Stack<Node> stack = new Stack<>();// 创建栈对象
stack.push(new Node(0,a.length-1));// 压栈,获得第一次排序的边界
while(!stack.empty()){// 当栈中空时,相当于所有的子递归全部实现
Node node = stack.pop();//出栈,取得当前排序的边界,相当于一次递归
int left = node.left;
int right = node.right;
boolean isRight = false;// 快速排序算法代码
// 降序排序
while(left < right){
if(a[left] < a[right]){
int t = a[left];
a[left] = a[right];
a[right] = t;
isRight = !isRight;
}
if(isRight){
right--;
}else{
left++;
}
}
// 通过压栈,实现类似递归的功能
if(right+1 < node.right){
stack.push(new Node(right+1,node.right));
}
if(left-1 > node.left){
stack.push(new Node(node.left,left-1));
}
}
}
}
class Node{// 封装类,存储一次排序的左右边界
public int left;
public int right;
public Node(int left, int right) {
super();
this.left = left;
this.right = right;
}
}
队列
队列是特殊的线性表,只允许在表的前端删除元素,在表的后端插入元素。所以队列是先进先出的数据结构。
- 为什么要使用队列:
- 计算机的任务调度系统
- 为了削减高峰时期订单请求,使用消息队列
- 其它数据结构比如树的广度优先遍历也需要借助队列来实现
- Android中用于实现线程间通信的消息队列是队列的典型应用之一
// 自定义类实现队列特性,在Java中,可以将LinkedList类用作队列
public class MyQueue<E> {
ArrayList<E> data = new ArrayList<E>();
public void push(E value){
data.add(value);
}
public E pop(){
E pop = data.remove(0);
return pop;
}
public boolean isEmpty(){
return data.size()==0;
}
public E peek(){
E peek = data.get(0);
return peek;
}
public int size(){
return data.size();
}
}
链表(单向链表)
线性表是最基本、最简单、也最常用的一种数据结构。线性表中数据元素之间的关系是一对一的关系,即除了第一个和最后一个数据元素之外,其它数据元素都是首尾相接的。
一个链表是由节点组成的,类似于火车一样。每个链表(火车)有n个节点(车厢),一个节点内(车厢内)一部分内存(空间)用来存储数据值(乘客),还有一部分内存用来存储指向下一个节点的地址(通向下一节车厢的门的钥匙),我们通过访问这个地址(钥匙)就可以得到下一个节点(到达下一节车厢)。
// 自定义类实现链表的模拟
public class Test06_LinkedList {
public static void main(String[] args) {
// 一个MyNode对象就是一个节点
MyNode<String> node = new MyNode<>("a");// 第一个节点
MyNode<String> head = node;// 头节点,火车头
// 第一个节点的next存放指向第二个节点的地址(钥匙)
node.next = new MyNode<>("b");
// 通过钥匙进入下一车厢
node = node.next;
// 第二个节点的next存放指向第三个节点的地址(钥匙)
node.next = new MyNode<>("c");
node = node.next;
// 回到头节点
node = head;
// 遍历链表
while(node!=null){
String str = node.value;// 取得节点存储的数据值
System.out.println(str);
node = node.next;// 跳向下一个节点
}
}
}
节点类
public class MyNode<T> {
public T value;// 节点中存储的数据值
public MyNode<T> next;// 节点中存储的指向下一个节点的引用(钥匙)
public MyNode(T value) {
super();
this.value = value;
}
@Override
public String toString() {
return value.toString();
}
}
- 链表中删除节点
public class Test07_LinkedList2 {
public static void main(String[] args) {
String[] data = {"a","b","c","d","e"};
MyNode<String> node = new MyNode<String>(data[0]);
MyNode<String> head = node;
for(int i=1;i<data.length;i++){
node.next = new MyNode<String>(data[i]);
node = node.next;
}
Scanner scanner = new Scanner(System.in);
int select;
do{
System.out.println("1-删除首节点");
System.out.println("2-删除尾节点");
System.out.println("3-删除值为c的节点");
System.out.println("4-退出");
select = scanner.nextInt();
switch(select){
case 1:
head = deleteFirstNode(head);// 删除首节点
myPrint(head);
break;
case 2:
head = deleteLastNode(head);// 删除尾节点
myPrint(head);
break;
case 3:
try {
head = deleteKeyNode(head,"e");// 删除指定值的节点
myPrint(head);
} catch (Exception e) {
System.out.println(e.getMessage());
}
break;
}
}while(select!=4);// 退出条件
scanner.close();
}
/**
* 删除指定值的节点
* @param head
* @throws Exception
*/
private static MyNode<String> deleteKeyNode(MyNode<String> head, String key) throws Exception {
if(head == null){
throw new Exception("链表不能为空");
}
if(head.value.equals(key)){// 删除首节点的情况
head = deleteFirstNode(head);
return head;
}
MyNode<String> node = head.next;
MyNode<String> preNode = head;
while(node.next!=null){
if(node.value.equals(key)){// 删除中间节点的情况
preNode.next = node.next;
break;
}
preNode = node;
node = node.next;
}
if(node.value.equals(key)){// 删除尾节点的情况
preNode.next = null;
return head;
}
if(node.next==null){
throw new Exception("删除节点不存在");
}
return head;
}
/**
* 遍历链表,打印信息
* @param node
*/
private static void myPrint(MyNode<String> node) {
while(node!=null){
System.out.println(node.value);
node = node.next;
}
}
/**
* 删除尾节点
* @param head
*/
private static MyNode<String> deleteLastNode(MyNode<String> head) {
if(head==null || head.next==null){
return null;
}
MyNode<String> result = head;
while(head.next.next != null){
head = head.next;
}// 找到倒数第二个节点
head.next = null;// 删除最后一个节点
return result;
}
/**
* 删除首节点
* @param head
* @return
*/
private static MyNode<String> deleteFirstNode(MyNode<String> head) {
if(head==null){
return null;
}
head = head.next;
return head;
}
}
练习
2.合并两个有序的链表,合并的链表仍然有序。
解题思路:
node1={1,3,5,7,9,12}
node2={2,4,6,8,10,11,13};
外循环遍历node2链表
内循环遍历node1链表,每次判断node2是否能插入到node1链表中,
不能则node1遍历至下一个节点。
能则退出内循环,将node2插入到node1链表中。
node2遍历至下一个节点。
public class HomeWork01 {
public static void main(String[] args) {
int[] arr1 = {1,3,5,7,9,12};
int[] arr2 = {2,4,6,8,10,11,13};
MyNode<Integer> head1 = createLinkedList(arr1);// 创建第一个链表
MyNode<Integer> head2 = createLinkedList(arr2);// 创建第二个链表
merge(head1, head2);
myPrint(head1);
}
/**
* 合并链表
* @param head1 被插入的目标链表
* @param head2 用来插入的数据链表
*/
private static void merge(MyNode<Integer> head1, MyNode<Integer> head2) {
while(head2!=null){
MyNode<Integer> node1=head1;// 获得被插入链表的首节点
int t = head2.value;// 获得要插入的链表的对应节点数据
MyNode<Integer> preNode=null;
while(node1!=null){
if(t <= node1.value){// 升序排序
break;
}
preNode = node1;
node1=node1.next;
}
MyNode<Integer> insert = new MyNode<>(t);// 通过要插入的数据创建新的节点
preNode.next = insert;// 将新的节点插入到目标链表中
insert.next = node1;
head2 = head2.next;// 遍历数据链表,获得下一个需要插入的值
}
}
/**
* 遍历链表,打印信息
* @param head1
*/
private static void myPrint(MyNode<Integer> head1) {
MyNode<Integer> node = head1;
while(node!=null){
System.out.print(node.value + " ");
node = node.next;
}
}
/**
* 创建链表
* @param arr1
* @return
*/
private static MyNode<Integer> createLinkedList(int[] arr1) {
MyNode<Integer> node = new MyNode<Integer>(arr1[0]);
MyNode<Integer> head = node;
for(int i=1;i<arr1.length;i++){
node.next = new MyNode<Integer>(arr1[i]);
node = node.next;
}
return head;
}
}
本文深入讲解了栈、队列及链表三种基本数据结构的特点与应用,并提供了详细的代码实例,包括利用栈实现快速排序的非递归算法、链表的基本操作及合并两个有序链表的算法。
1172

被折叠的 条评论
为什么被折叠?



