数组(Array)是一种线性表数据结构。它用一组连续的内存空间,来存储一组具有相同类型的数据。
线性表就是数据排成像一条线一样的结构。每个线性表上的数据最多只有前和后两个方向。其实除了数组,链表、队列、栈等也是线性表结构。而与它相对立的概念是非线性表,比如二叉树、堆、图等。之所以叫非线性,是因为,在非线性表中,数据之间并不是简单的前后关系。
面试问题:数组与链表主要区别 链表适合插入、删除,时间复杂度是O(1),而数组支持随机访问,根据下表随机访问的时间复杂度为O(1);
数组需要连续的储存空间,而链表不需要连续的存储空间,它是通过指针将一组连续的内存块串联起来, 比如申请的是 100MB 大小的链表,不会有问题。这里把内存块称为节点,它不仅要储存数据,而且需要记录下一个节点的地址,把记录下一个节点地址称为后继指针next。
链表第一个节点称为头结点,最后一个节点称为尾节点,头结点用于记录链表的基地址,就可以遍历整个链表,而尾节点不是指向下一个节点,而是指向空地址NUll
。
并且链表插入、删除时间复杂度为O(1),而随机访问时间复杂度是O(n)。
next
指向后面的结点。而双向链表,它支持两个方向,每个结点不止有一个后继指针 next
指向后面的结点,还有一个前驱指针 prev
指向前面的结点;Java
中的LinkedHashMap
就采用双向链表数据结构方法一:递归反转法,在反转当前节点之前先反转后续节点。这样从头结点开始,层层深入直到尾结点才开始反转指针域的指向。简单的说就是从尾结点开始,逆向反转各个结点的指针域指向。
public class SimpleLinkList {
public static void main(String[] args) {
Node head = new Node(0);
Node node1 = new Node(1);
Node node2 = new Node(2);
Node node3 = new Node(3);
head.setNext(node1);
node1.setNext(node2);
node2.setNext(node3);
// 打印反转前的链表,定义一个h保存head节点数据,否则打印代码执行完毕head就为空了,下面反转代码就无法执行
Node h = head;
while (null != h) {
System.out.print(h.getData() + " ");
h = h.getNext();
}
// 调用反转方法
head = Reverse1(head);
System.out.println("\n**************************");
// 打印反转后的结果
while (null != head) {
System.out.print(head.getData() + " ");
head = head.getNext();
}
}
/**
* 递归,在反转当前节点之前先反转后续节点
*/
public static Node Reverse1(Node head) {
// head看作是前一结点,head.getNext()是当前结点,reHead是反转后新链表的头结点
if (head == null || head.getNext() == null) {
return head;// 若为空链或者当前结点在尾结点,则直接返回
}
Node reHead = Reverse1(head.getNext());// 先反转后续节点head.getNext()
head.getNext().setNext(head);// 将当前结点的指针域指向前一结点
head.setNext(null);// 前一结点的指针域令为null;
return reHead;// 反转后新链表的头结点
}
}
class Node {
private int Data;// 数据域
private Node Next;// 指针域
public Node(int Data) {
this.Data = Data;
}
public int getData() {
return Data;
}
public void setData(int Data) {
this.Data = Data;
}
public Node getNext() {
return Next;
}
public void setNext(Node Next) {
this.Next = Next;
}
}
方法二:
public static Node reverse(Node list) {
Node curr = list, pre = null;
while (curr != null) {
Node next = curr.next;
curr.next = pre;
pre = curr;
curr = next;
}
return pre;
}
例如这样的链表:A->B->C->D->B->C->D, 当遍历到节点D的时候,我们需要比较的是之前的节点A、B、C,不存在相同节点。这时候要遍历的下一个新节点是B,B之前的节点A、B、C、D中恰好也存在B,因此B出现了两次,判断出链表有环。 采取了快慢指针:
public static boolean checkCircle(Node list){
if(list==null) return false;
Node fastNode=list.next;
Node slowNode = list;
while(fastNode!=null&&fastNode.next!=null){
fastNode = fastNode.next.next;
slowNode = slowNode.next;
if(fastNode==slowNode) return true;
}
return false;
}
public static Node findMiddleNode(Node list){
if (list == null) return null;
//fast 节点存储着list
Node fast = list;
Node slow = list;
while (fast.next != null && fast.next.next != null) {
//fast节点存储着fast下下节点内存地址
fast = fast.next.next;
slow = slow.next;
}
return slow;
}
public static Node mergeSortNode(Node la,Node lb){
if(la==null) return lb;
if(lb==null) return la;
Node pNode = la;
Node qNode = lb;
Node head;
if(pNode.data<qNode.data){
head = pNode;
pNode = pNode.next;
}else {
head = qNode;
qNode = qNode.next;
}
Node rNode= head;
while(pNode!=null&&qNode!=null){
if(pNode.data<qNode.data){
rNode.next = pNode;
pNode = pNode.next;
}else {
rNode.next = qNode;
qNode = qNode.next;
}
rNode = rNode.next;
}
if(pNode!=null){
rNode.next =pNode;
}else {
rNode.next = qNode;
}
return head;
}
public static Node deleteLastNode(Node list, int k){
Node fast = list;
int i=1;
while(fast!=null&&i<k){
fast = fast.next;
++i;
}
if(fast==null) return null;
Node slow = list;
Node preve = null;
while(fast.next!=null){
fast = fast.next;
preve = slow;
slow = slow.next;
}
if(preve==null){
list = list.next;
}else {
preve.next = preve.next.next;
}
return list;
}
/**
* 单链表插入与删除
* @author zhangtianzhu
*
*/
public class SingleListDemo {
public static void main(String[] args) {
// TODO Auto-generated method stub
SingleListDemo sDemo = new SingleListDemo();
Node head = null;
//新增结点,第一个新增需要返回头指针
head = sDemo.insertNode(1, head);
sDemo.insertNode(2, head);
sDemo.insertNode(3, head);
sDemo.printListNode(head);
System.out.println(sDemo.getNodeLength(head));
//删除索引为2的节点
if((head = sDemo.deleteNode(2, head))!=null){
sDemo.printListNode(head);
}
//删除头结点
if((head = sDemo.deleteNode(1, head))!=null){
sDemo.printListNode(head);
}
}
/**
* 插入节点
* @param data
* @param head
* @return
*/
public Node insertNode(int data,Node head){
Node node = new Node(data);
if(head==null){
head = node;
return head;
}
Node curNode = head;
//循环找到当前尾结点
while(curNode.next!=null){
curNode = curNode.next;
}
//尾结点指针指向新增结点
curNode.next = node;
return head;
}
/**
* 单链表长度
* @param head
* @return
*/
public int getNodeLength(Node head){
Node node = head;
int length = 0;
while(node!=null){
node = node.next;
length++;
}
return length;
}
/**
* 删除结点
* @param index
* @param head
* @return
*/
public Node deleteNode(int index,Node head){
if(index<1||index>getNodeLength(head)){
return null;
}
//删除头结点
if(index==1){
head = head.next;
return head;
}
//从头结点下一个结点遍历
Node curNode = head.next;
//记录当前循环上一个节点用于删除结点
Node preNode = head;
int i = 2;
while(curNode.next!=null){
if(i==index){
//删除结点,前一个结点指向当前删除的下一个结点
preNode.next = curNode.next;
}else {
preNode = curNode;
curNode = curNode.next;
}
i++;
}
return head;
}
public void printListNode(Node head){
Node curNode = head;
while(curNode!=null){
System.out.print(curNode.getData()+" ");
curNode = curNode.next;
}
System.out.println();
}
}
class Node{
int data;
Node next = null;
public Node(int data) {
// TODO Auto-generated constructor stub
this.data = data;
}
public int getData() {
return data;
}
}