1、什么是LinkedList
LinkedList的底层是双链表结构,由于链表没有将元素存储在连续的空间,元素存在单独的节点中,然后通过引用将节点连接起来,因此在任意位置删除或添加元素时,不需要搬移元素,效率比较高。
2、说明
(1)LinkedList实现了List接口
(2)LinkedList使用了双链表结构
(3)LinkedList没有实现RandomAcess接口,因此不支持随机访问
(4)LinkedList插入根删除效率比较高,时间复杂度为O(1);
(5)LinkedList比较适合在任意节点插入的场景
3、用代码实现一个双链表
3.1、定义双链表的一个类,然后再定义它的头节点和尾节点
//定义双向链表的节点
public static class ListNode{
int value;
ListNode prev;
ListNode next;
public ListNode(int val){
this.value=val;
}
}
//头节点
public ListNode head;
//尾节点
public ListNode tail;
3.2、头插法
//头插法
public void addFirst(int data){
ListNode node=new ListNode(data);
if(head==null){
//如果链表头为空
head=node;
tail=node;
}else{
node.next=head;
head.prev=node;
head=node;
}
}
3.3、尾插法
//尾插法
public void addLast(int data){
ListNode node=new ListNode(data);
if(head==null){
//如果链表为空
head=node;
}else{
tail.next=node;
node.prev=tail;
}
tail=node;
}
3,4实现一个任意节点插入,第一个数据节点为0号下标
public void addIndex(int index,int data){
//校验index的合法性
CheckRange(index);
//下标为0的时候直接用头插法
if(index==0){
addFirst(data);
return;
}
//下标为size的时候直接用尾插法
if(index==size()){
addLast(data);
return;
}
// 处理中间位插入的时候要先找到对应下标的前一个节点
ListNode prevNode = findIndex(index);
// 创建一个新节点
ListNode node = new ListNode(data);
// 让这个新节点的next引用prevNode.next
node.next = prevNode.next;
// 让prevNode.next引用新节点
prevNode.next = node;
}
private ListNode findIndex(int index) {
ListNode current=head;
while(index<0){
current=current.next;
index--;
}
return current;
}
public int size() {
//直接遍历节点
ListNode current=head;
int count=0;
while(current!=null){
current=current.next;
count++;
}
return count;
}
private void CheckRange(int index) {
if(index<0||index>size()){
throw new RuntimeException("插入位置不合法,index="+index);
}
}
3.5、查找关键字是否包含在链表中
public boolean contains(int key){
ListNode current=head;
while(current!=null){
if(current.value==key){
return true;
}
current=current.next;
}
return false;
}
3.6、删除第一次出现key的关键字
public void remove(int key){
ListNode current=head;
while(current!=null){
//头节点
if(current.value==key) {
if (current==head) {
//遍历进来之后删除头节点
head = head.next;
if (head == null) {
//如果链表已经是空
tail = null;
}else{
//只剩下一个节点
head.prev.next=null;
head.prev=null;
}
} else if (current==tail) {
//删除尾节点
tail=tail.prev;
tail.next=null;
}else{
//key在链表中间
current.prev.next=current.next;
current.next.prev=current.prev;
}
//删除完成就返回
return;
}
//继续遍历
current=current.next;
}
}
3.7、删除所有值为key的节点
public void removeAll(int key){
ListNode current=head;
while(current!=null){
//头节点
if(current.value==key) {
if (current==head) {
//遍历进来之后删除头节点
head = head.next;
if (head == null) {
//如果链表已经是空
tail = null;
}else{
//只剩下一个节点
head.prev=null;
}
} else if (current==tail) {
//删除尾节点
tail=tail.prev;
tail.next=null;
}else{
//key在链表中间
current.prev.next=current.next;
current.next.prev=current.prev;
}
}
//继续遍历
current=current.next;
}
}
3.8、
打印显示1
public void dispaly(){
StringBuilder sb=new StringBuilder();
sb.append("[");
ListNode current=head;
while(current!=null){
sb.append(current.value);
if(current.next!=null){
sb.append(",");
}
current=current.next;
}
sb.append("]");
System.out.println(sb);
}
打印显示2
public void printList(){
//判断节点是否为空
if(head==null){
System.out.println();
return;
}
//定义一个栈,用来保存每一个节点
Stack<ListNode> stack=new Stack<>();
//开始遍历,定义遍历节点
ListNode current=head;
while(current!=null){
//链表元素入栈
stack.push(current);
current=current.next;
}
//遍历完成之后,所有的元素都在栈里,
while(!stack.isEmpty()){
//栈中元素依次出栈
System.out.println(stack.pop().value+" ");
}
}
3.9、清空链表
public void clear(){
head=null;
}
3.10测试类测试LinkedList
public class TestDemo1 {
public static void main(String[] args) {
Demo1 demo1 = new Demo1();
System.out.println();
demo1.dispaly();
demo1.addFirst(1);
demo1.addLast(12);
demo1.addLast(23);
demo1.addLast(34);
System.out.println("====");
demo1.addLast(45);
demo1.addLast(56);
demo1.addLast(45);
demo1.dispaly();
System.out.println(demo1.size());
System.out.println("是否包含=>");
System.out.println(demo1.contains(23));
System.out.println(demo1.contains(89));
System.out.println("增加元素=>");
demo1.addIndex(3, 45);
demo1.dispaly();
System.out.println("删除元素=>");
demo1.remove(12);
demo1.removeAll(45);
demo1.dispaly();
}
}
3.11、测试结果
4、LinkedList和ArrayList的区别
5、栈
5.1、什么是栈
栈:栈是一种特殊的线性表,其只允许在一端进行插入和删除元素操作,进行数据插入和删除操作的一端为栈顶,另外一端为栈底。栈中元素遵循先进后出的原则
5.2、压栈和出栈
压栈:栈的插入操作叫做进栈/入栈,入数据在栈顶
出栈:栈的删除操作叫做出栈,出数据在栈顶
5.3、栈的实现
import java.util.Arrays;
public class MyStack {
private int[] elementData;
private int size;
private final int DEFAULT_CAPACITY=7;
public MyStack(){
this.elementData=new int[DEFAULT_CAPACITY];
}
public MyStack(int capacity){
if(capacity<0){
throw new RuntimeException("数组容量不能小于零");
} else if (capacity>0) {
this.elementData=new int[capacity];
}else{
this.elementData=new int[DEFAULT_CAPACITY];
}
}
//入栈操作
public void push(int data){
//是否需要扩容
checkRange(data);
//在size里面添加元素
elementData[size]=data;
size++;
}
//出栈操作
public int pop(){
int top=peek();
size--;
return top;
}
//找到栈顶元素
private int peek() {
//判断是否为空
if(size==0){
throw new RuntimeException("栈为空");
}
return elementData[size-1];
}
private void checkRange(int data) {
if(elementData.length==size){
this.elementData= Arrays.copyOf(this.elementData,2*elementData.length);
}
}
public int size(){
return size;
}
public boolean empty(){
return size==0;
}
}
public class TestStack {
public static void main(String[] args) {
Stack<Integer> s1=new Stack<>();
s1.push(12);
s1.push(23);
s1.push(34);
s1.push(45);
s1.push(56);
s1.push(67);
System.out.println(s1);
s1.pop();
s1.pop();
System.out.println(s1);
//查看栈顶元素
int top=s1.peek();
System.out.println("查看栈顶元素,top="+top);
//弹出栈顶元素
int pop=s1.pop();
System.out.println("查看出栈元素,pop="+pop);
System.out.println(s1);
System.out.println(s1.size());
while(s1.isEmpty()==false){
System.out.println(s1.pop());
}
System.out.println(s1);
System.out.println(s1.size());
//s1.pop();此时栈已经为空,再去pop栈中元素或者peek()栈中元素,就会抛出异常
}
}
5.4栈的使用:算法实例
括号匹配问题:力扣
public boolean isVaild(String s){
if(s==null||s.equals("")){
return false;
}
//1、定义一个栈用来存入左括号
Stack<Character> stack=new Stack<>();
//2、开始遍历字符串
for (int i = 0; i <s.length(); i++) {
char ch=s.charAt(i);
//判断是否为左括号
if(ch=='{'||ch=='('||ch=='['){
//如果是左括号,入栈
stack.push(ch);
}else{
//检查一下栈是否为空,如果直接为空返回false
if(stack.isEmpty()){
return false;
}
//栈不为空,获取栈顶元素与当前右括号进行匹配
char top=stack.peek();
if(top==']'||top=='}'||top==')'){
//出栈栈顶元素
stack.pop();
}else{
return false;
}
}
}
//检查一下栈是否为空
if(!stack.isEmpty()){
return false;
}
return true;
}
逆波兰表达式:力扣
/**
* 逆波兰表达式
* @param tokens
* @return
*/
public int evalPAN(String[] tokens){
//判断是否为空
if(tokens==null||tokens.length==0){
return -1;
}
//创建一个栈,用来存放元素
Stack<Integer> stack=new Stack<>();
//开始遍历字符数组
for (int i = 0; i < tokens.length; i++) {
String str=tokens[i];
boolean b=isoperator(str);
if(b){
int left=stack.pop();
int right=stack.pop();
switch(str){
case"+":
stack.push(left+right);
break;
case"-":
stack.push(left-right);
break;
case"*":
stack.push(left*right);
break;
case"/":
stack.push(left/right);
break;
}
}else{
stack.push(Integer.valueOf(str));
}
}
return stack.pop();
}
private boolean isoperator(String str) {
//判断是否是操作符
if(str==null&&str.equals("")){
return false;
}
if(str.equals("+")||str.equals("-")||str.equals("*")||str.equals("/")){
return true;
}
return false;
}
栈的压入和弹出序列:栈的压入、弹出序列_牛客题霸_牛客网
/**
* 栈的压入,弹出序列
* @param pushA
* @param popA
* @return
*/
public boolean IsPopOrder(int [] pushA,int [] popA) {
//先检查数组pushA是否为空
if(pushA==null||pushA.length==0){
return false;
}
//定义一个栈用来存放数组元素
Stack<Integer> stack=new Stack<>();
//定义一个变量j,防止被循环修改
int j=0;
//遍历数组元素pushA
for(int i=0;i<pushA.length;i++){
stack.push(pushA[i]);
while(!stack.isEmpty()&&j<popA.length&&stack.peek()==popA[j]){
j++;
stack.pop();
}
}
return stack.isEmpty();
}
最小栈:力扣
/**
* 最小栈
*/
public class minStack {
//创建两个栈
Stack<Integer> stack;
Stack<Integer> MinStack;
//初始化堆栈对象
public minStack(){
stack=new Stack<>();
MinStack=new Stack<>();
}
public void push(int val){
//将元素直接入栈到satck,无条件入栈
stack.push(val);
//判断MinStack是否为空,获取val小于MinStack的栈顶元素,直接入栈
if(MinStack.isEmpty()||val<MinStack.peek()){
MinStack.push(val);
}
}
public void pop(){
//删除栈顶元素
//首先判断stack是否为空,如果为空直接返回
if(stack.isEmpty()){
return;
}
int value=stack.pop();
if(value==MinStack.peek()&&MinStack.isEmpty()){
MinStack.pop();
}
}
public int top(){
//获取satck的堆顶元素
if(stack.isEmpty()){
return -1;
}
return stack.peek();
}
public int getMin(){
//获取最小栈的堆顶元素
if(MinStack.isEmpty()){
return -1;
}
return stack.peek();
}
}