链表
哈希表和有序表并不是链表的部分,但是在解决算法题中经常可以借助这样的数据结构!
1、哈希表
- 哈希表包括HashSet和HashMap,二者只有key是否携带value的区别
- 哈希表的增删改查的时间复杂度可以认为是O(1),但是这个常数时间比数组的常数时间要大
- 放入哈希表的东西,如果是基础类型,内部按值传递,内存占用就是这个东西的大小,如果不是基础类型,内部按引用传递,内存占用是这个东西内存地址的大小,固定8字节。
package com.zwj.study.linkedlist;
import java.util.HashMap;
import java.util.HashSet;
public class HashTable {
public static void main(String[] args) {
HashSet hashSet = new HashSet<Integer>();
hashSet.add(1);
hashSet.add(2);
hashSet.add(3);
System.out.println(hashSet.contains(1));
System.out.println(hashSet.isEmpty());
hashSet.remove(1);
System.out.println(hashSet.contains(1));
HashMap hashMap = new HashMap<Integer,String>();
hashMap.put(1,"我是1");
hashMap.put(2,"我是2");
hashMap.put(3,"我是3");
System.out.println(hashMap.containsKey(1));
hashMap.remove(1);
System.out.println(hashMap.containsKey(1));
System.out.println(hashMap.get(2));
}
}
2、有序表
- 有序表包括TreeSet和TreeMap,二者只有key是否携带value的区别
- 有序表和哈希表的区别是,有序表把key按照顺序组织起来,而哈希表完全不组织
- 红黑树、AVL树、size-balance-tree和跳表等都属于有序表结构,只是底层具体实现不同
- 放入有序表的东西,如果是基础类型,内部按值传递,内存占用就是这个东西的大小,如果不是基础类型,必须提供比较器,内部按引用传递,内存占用是这个东西内存地址的大小,固定8字节。
package com.zwj.study.linkedlist;
import java.util.*;
public class HashTable {
public static void main(String[] args) {
class MyRule implements Comparator<Node> {
@Override
public int compare(Node o1, Node o2) {
return o1.key - o2.key;
}
}
Node node1 = new Node(1);
Node node2 = new Node(1);
Node node3 = new Node(3);
TreeSet treeSet = new TreeSet<Node>(new MyRule());
treeSet.add(node1);
treeSet.add(node2);
treeSet.add(node3);
System.out.println(treeSet.contains(node2));
treeSet.remove(node2);
System.out.println(treeSet.contains(node2));
TreeMap treeMap = new TreeMap<Node,String>(new MyRule());
treeMap.put(new Node(3),"我是3");
treeMap.put(new Node(8),"我是8");
treeMap.put(new Node(6),"我是6");
treeMap.put(new Node(7),"我是7");
treeMap.put(new Node(5),"我是5");
treeMap.put(new Node(4),"我是4");
treeMap.put(new Node(9),"我是9");
System.out.println(treeMap.firstKey());
System.out.println(treeMap.lastKey());
System.out.println(treeMap.get(new Node(5)));
System.out.println(treeMap.ceilingKey(new Node(2)));
System.out.println(treeMap.floorKey(new Node(10)));
}
}
class Node{
int key;
String value;
public Node(int key){
this.key = key;
}
public Node(int key,String value){
this.key = key;
this.value = value;
}
@Override
public String toString() {
return "key->" + this.key + "---value->" + this.value;
}
}
3、单链表
- 单链表是一种链式存取的数据结构,用一组地址任意的存储单元存放线性表中的数据元素。链表中的数据是以节点来表示的,每个节点的构成:data域(数据元素)+next域(下一个结点的存储位置)。
- 单链表与数组相比的最大差别是:单链表的数据元素存放在内存空间的地址是不连续的,因此查找或者遍历的时候需要从头结点依次后移。
- 常见的需要实现的方法有头插、尾插、固定索引插、删除、查找
package com.zwj.study.linkedlist;
class Test{
public static void main(String[] args) {
Node head = new Node(1,"刘备");
/*
尾插法
*/
System.out.println(SingleLinkedList.size(head));
Node node = new Node(2,"关羽");
SingleLinkedList.lastAdd(head,node);
SingleLinkedList.list(head);
System.out.println(SingleLinkedList.size(head));
/*
头插法
*/
System.out.println("--------------");
node = new Node(3,"张飞");
head = SingleLinkedList.firstAdd(head,node);
SingleLinkedList.list(head);
/*
固定节点插入法
由于不知道固定节点插入是否是头插,因此设置返回值
*/
System.out.println("---------------");
node = new Node(4,"赵云");
head = SingleLinkedList.add(1,head,node);
SingleLinkedList.list(head);
/*
在链表中删除id为1的数据
由于不知道删除的是否是头结点,因此设置返回值
*/
System.out.println("---------------");
head = SingleLinkedList.delete(1,head);
SingleLinkedList.list(head);
/*
在链表中查询id为2的节点的值
*/
System.out.println("---------------");
System.out.println(SingleLinkedList.find(2,head));
}
}
public class SingleLinkedList {
public static Node firstAdd(Node head,Node node){
node.next = head;
return node;
}
public static void lastAdd(Node head,Node node){
while (head.next != null){
head = head.next;
}
head.next = node;
}
public static Node add(int index,Node head,Node node){
Node temp = head;
int num = 0;
if (index == 0){
return SingleLinkedList.firstAdd(head,node);
} else {
while (head != null){
if (num == index - 1){
node.next = head.next;
head.next = node;
break;
}
num++;
head = head.next;
}
}
return temp;
}
public static Node delete(int index,Node head){
Node temp = head;
if (index == 0){
return head.next;
} else {
while (head != null){
if (head.next.id == index){
head.next = head.next.next;
break;
}
head = head.next;
}
}
return temp;
}
public static Node find(int index,Node head){
while (head.id != index){
head = head.next;
}
return head;
}
public static void list(Node head){
while (head != null){
System.out.println(head.toString());
head = head.next;
}
}
public static int size(Node head){
int num = 0;
while (head != null){
num++;
head = head.next;
}
return num;
}
}
class Node{
int id;
String name;
Node next;
public Node(int id,String name){
this.id = id;
this.name = name;
}
@Override
public String toString() {
return "id---->" + this.id + ",name---->" + this.name;
}
}
4、双向链表
- 双向链表也叫双链表,是链表的一种,它的每个数据结点中都有两个指针,分别指向直接后继和直接前驱。所以,从双向链表中的任意一个结点开始,都可以很方便地访问它的前驱结点和后继结点。
- 双向链表不是循环链表,与单链表相比它只是增加了前驱节点
package com.zwj.study.linkedlist;
class Test2{
public static void main(String[] args) {
Node2 head = new Node2(1,"刘备");
System.out.println("--------");
Node2 node = new Node2(2,"关羽");
BidirectionalLinkedList.firstAdd(head,node);
BidirectionalLinkedList.list(head);
}
}
public class BidirectionalLinkedList {
public static void firstAdd(Node2 head,Node2 node){
while (head.before != null){
head = head.before;
}
node.next = head;
head.before = node;
}
public static void list(Node2 node){
while (node.before != null){
node = node.before;
}
while (node != null){
System.out.println(node);
node = node.next;
}
}
}
class Node2{
int id;
String name;
Node2 before;
Node2 next;
public Node2(int id,String name){
this.id = id;
this.name = name;
}
@Override
public String toString() {
return "id---->" + this.id + ",name---->" + this.name;
}
}
5、练习
2. 两数相加 - 力扣(Leetcode)
class Solution {
public ListNode addTwoNumbers(ListNode l1, ListNode l2) {
ListNode pre = new ListNode(0);
ListNode cur = pre;
int carry = 0;
while (l1 != null || l2 != null){
int x = l1 == null ? 0 : l1.val;
int y = l2 == null ? 0 : l2.val;
int sum = x + y + carry;
carry = sum / 10;
sum = sum % 10;
cur.next = new ListNode(sum);
cur = cur.next;
if (l1 != null)
l1 = l1.next;
if (l2 != null)
l2 = l2.next;
}
if (carry == 1){
cur.next = new ListNode(1);
}
return pre.next;
}
}
19. 删除链表的倒数第 N 个结点 - 力扣(Leetcode)
class Solution {
public ListNode removeNthFromEnd(ListNode head, int n) {
int num = 0;
ListNode temp = head;
while (temp != null){
num++;
temp = temp.next;
}
temp = head;
int result = num - n;
if (result == 0){
return head.next;
}
for (int i = 0; i < result - 1; i++){
head = head.next;
}
head.next = head.next.next;
return temp;
}
}
24. 两两交换链表中的节点 - 力扣(Leetcode)
class Solution {
public ListNode swapPairs(ListNode head) {
if (head == null || head.next == null){
return head;
}
ListNode node = head.next;
head.next = swapPairs(node.next);
node.next = head;
return node;
}
}
61. 旋转链表 - 力扣(Leetcode)
class Solution {
public ListNode rotateRight(ListNode head, int k) {
ListNode temp = head;
if (head == null || head.next == null || k == 0){
return head;
}
ArrayList list = new ArrayList<ListNode>();
int num = 0;
while (head != null){
list.add(head);
num++;
head = head.next;
}
k = k % num;
if (k == 0){
return temp;
}
ListNode node = (ListNode)list.get(num - k);
temp = node;
for (int i = num - k + 1; i < num; i++) {
node.next = (ListNode)list.get(i);
node = node.next;
}
for (int i = 0; i < num - k; i++) {
node.next = (ListNode)list.get(i);
node = node.next;
}
node.next = null;
return temp;
}
}
82. 删除排序链表中的重复元素 II - 力扣(Leetcode)
class Solution {
public ListNode deleteDuplicates(ListNode head) {
if (head == null || head.next == null){
return head;
}
ListNode temp = head;
TreeMap treeMap = new TreeMap<Integer,Integer>();
while (temp != null){
if (treeMap.containsKey(temp.val)){
treeMap.put(temp.val,(int)treeMap.get(temp.val)+1);
} else {
treeMap.put(temp.val,1);
}
temp = temp.next;
}
ListNode node = head;
temp = node;
Set<Integer> keys = treeMap.keySet();
for (int key : keys){
if ((int)treeMap.get(key) == 1){
node.next = new ListNode(key);
node = node.next;
}
}
node.next = null;
return temp.next;
}
}
更优解:
92. 反转链表 II - 力扣(Leetcode)
class Solution {
public ListNode reverseBetween(ListNode head, int left, int right) {
if (head == null || left == right || head.next == null){
return head;
}
int len = 0;
ListNode node = head;
ListNode fpre = null;
ListNode tpos = null;
while (node != null){
len++;
fpre = len == left - 1 ? node : fpre;
tpos = len == right + 1 ? node : tpos;
node = node.next;
}
node = fpre == null ? head : fpre.next;
ListNode listNode = node;
ListNode newHead = null;
ListNode next;
while (node != tpos){
next = node.next;
node.next = newHead;
newHead = node;
node = next;
}
listNode.next = tpos;
if (fpre == null){
return newHead;
} else {
fpre.next = newHead;
return head;
}
}
}
142. 环形链表 II - 力扣(Leetcode)
class Solution {
public ListNode detectCycle(ListNode head) {
if (head == null){
return null;
}
ListNode pre = null;
ListNode cur = head;
HashSet hashSet = new HashSet<ListNode>();
while (cur != null){
if (hashSet.contains(cur)){
pre = cur;
break;
} else {
hashSet.add(cur);
cur = cur.next;
}
}
return pre;
}
}
143. 重排链表 - 力扣(Leetcode)
class Solution {
public void reorderList(ListNode head) {
if (head.next == null || head.next.next == null){
return;
}
List list = new ArrayList<ListNode>();
ListNode node = head;
while (node != null){
list.add(node);
node = node.next;
}
int len = list.size();
ListNode temp;
for (int i = 0; i < len / 2; i++) {
temp = head.next;
head.next = (ListNode)list.get(len - i - 1);
head = head.next;
head.next = temp;
head = head.next;
}
head.next = null;
}
}
148. 排序链表 - 力扣(Leetcode)
class Solution {
public ListNode sortList(ListNode head) {
if (head == null || head.next == null){
return head;
}
ListNode pre = new ListNode(0,head);
ListNode cur = head;
TreeMap treeMap = new TreeMap<Integer,Integer>();
while (cur != null){
if (treeMap.containsKey(cur.val)){
treeMap.put(cur.val,(int)treeMap.get(cur.val) + 1);
} else {
treeMap.put(cur.val,1);
}
cur = cur.next;
}
cur = pre;
Set<Integer> keys = treeMap.keySet();
for (int key : keys){
while ((int)treeMap.get(key) > 0)
{
treeMap.put(key,(int)treeMap.get(key) - 1);
pre.next = new ListNode(key);
pre = pre.next;
}
}
pre.next = null;
return cur.next;
}
}
114. 二叉树展开为链表 - 力扣(Leetcode)
class Solution {
public void flatten(TreeNode root) {
TreeNode cur = root;
while (cur != null){
if (cur.left != null){
TreeNode next = cur.left;
TreeNode temp = next;
while (temp.right != null){
temp = temp.right;
}
temp.right = cur.right;
cur.left = null;
cur.right = next;
}
cur = cur.right;
}
}
}
328. 奇偶链表 - 力扣(Leetcode)
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Fp5j9EjM-1672736345020)(null)]
class Solution {
public ListNode oddEvenList(ListNode head) {
if (head == null || head.next == null || head.next.next == null){
return head;
}
ListNode next = head.next;
ListNode cur = head;
ListNode temp = head.next;
while (next != null && next.next != null){
cur.next = next.next;
cur = cur.next;
next.next = cur.next;
next = next.next;
}
cur.next = temp;
return head;
}
}
23. 合并K个升序链表 - 力扣(Leetcode)
class Solution {
public ListNode mergeKLists(ListNode[] lists) {
if (lists.length == 0){
return null;
}
TreeMap treeMap = new TreeMap<Integer,Integer>();
for (int i = 0; i < lists.length; i++) {
while (lists[i] != null){
if (treeMap.containsKey(lists[i].val)){
treeMap.put(lists[i].val,(int)treeMap.get(lists[i].val) + 1);
} else {
treeMap.put(lists[i].val,1);
}
lists[i] = lists[i].next;
}
}
ListNode pre = new ListNode(0,new ListNode());
ListNode cur = pre;
Set<Integer> keys = treeMap.keySet();
for (int key : keys){
while ((int)treeMap.get(key) > 0){
cur.next = new ListNode(key);
cur = cur.next;
treeMap.put(key,(int)treeMap.get(key) - 1);
}
}
cur.next = null;
return pre.next;
}
}
25. K 个一组翻转链表 - 力扣(Leetcode)
class Solution {
public ListNode reverseKGroup(ListNode head, int k) {
if (head == null || k == 1){
return head;
}
ListNode dummy = new ListNode(0,head);
ListNode start = dummy;
ListNode end = dummy;
while (true){
for (int i = 0; i < k && end != null; i++) {
end = end.next;
}
if (end == null){
break;
}
ListNode startNext = start.next;
ListNode endNext = end.next;
end.next = null;
start.next = reverseListNodeList(start.next);
startNext.next = endNext;
start = end = startNext;
}
return dummy.next;
}
private ListNode reverseListNodeList(ListNode head){
ListNode cur = head;
ListNode pre = null;
while (cur != null){
ListNode next = cur.next;
cur.next = pre;
pre = cur;
cur = next;
}
return pre;
}
}