一、数据结构和算法的重要性
目前程序员的门槛越来越高,对于已经参加工作的程序员来说,面试或者笔试被问到数据结构和算法已经屡见不鲜,尤其是一些一线的IT公司,数据结构和算法的面试题肯定会有,要知道“算法是程序的灵魂”。作为一名程序员,如果你不想一辈子停留在“码农”这样的标签里,那么就请花些时间去提升自己的数据结构和算法方面的知识吧。
二、数据结构
数据结构分为线性结构和非线性结构。
线性结构作为最常用的数据结构,其特点是数据元素中存在一对一的线性关系,线性结构有两种不同的存储结构,即顺序存储结构和链式存储结构。
顺序存储的线性表称为顺序表,顺序表中的存储元素是连续的;链式存储的线性表叫做链表,链表中的存储元素不一定是连续的,元素节点中存放数据元素以及相邻元素的地址信息。常见的线性结构有:数组、队列、链表和栈
非线性结构包括:二维数组,多维数组,广义表,树结构,图结构(这里暂时不过多的进行介绍)
三、链表结构
下图是链表在内存中的存储示意图
1)链表以节点方式进行链式存储
2)每个节点都含有data域和next域。data域存储数据,next域指向下个数据
3)链表分为带头节点和不带头节点的链表。(上图表示带头结点)
四、单链表实现
下面话不多说,我们用java代码实现一个单链表,并进行增删改查的操作
1)首先定义节点(Node)
class Node {
// 元素下标
int index;
// data域,存储数据
Object data;
// 指向下一个节点
Node next;
public Node(int index, Object data) {
this.index = index;
this.data = data;
}
@Override
public String toString() {
return "Node{" +
"index=" + index +
", data=" + data +
'}';
}
}
2)定义头节点,实现增删改查的方法
public class SignleLinkedList {
// 创建一个头节点
private static final Node HEADNODE = new Node(-1, null);
private Node getHead() {
return HEADNODE;
}
// 添加
public void add(Object data) {
//将temp当做一个指针,一开始指向链表的头部
Node temp = getHead();
while (true) {
//当前所指对象的下一个是否为空,为空就代表,此指针已经指到了链表的最后一个元素了
if (temp.next == null) {
break;
}
//将temp指针后移,一定要移动,否则就是死循环
temp = temp.next;
}
// 将要添加的数据封装成节点
Node node = new Node(temp.index + 1, data);
// 进行数据插入
node.next = temp.next;
temp.next = node;
}
// 修改
public void set(int index, Object data) {
if (HEADNODE.next == null) {
System.out.println("该链表为空~");
return;
}
Node temp = getHead();
// falg标记是否找到了要修改的元素
boolean flag = false;
while (true){
//temp为null的时候表示链表已经到了尾部
if (temp == null) {
break;
}
if (temp.index == index){
flag = true;
break;
}
temp = temp.next;
}
if (flag) {
//修改元素属性
temp.data = data;
} else {
System.out.println("没有找到该人物~");
}
}
public void remove(int index) {
//temp为指针作用
Node temp = HEADNODE;
//flag用来标记是否找到需要删除的元素
boolean flag = false;
while (true) {
if (temp.next == null) {
break;
}
if (temp.next.index == index) {
//找到对应的元素之后,用flag标记一下
flag = true;
break;
}
temp = temp.next;
}
if (flag) {
// 找到之后将要删除的元素的上一个节点的next直接指向删除元素的next
temp.next = temp.next.next;
} else {
System.out.println("没有找到该元素");
}
}
public void findAll() {
if (HEADNODE.next == null) {
System.out.println("链表为空~");
return;
}
StringBuffer datas = new StringBuffer(64);
datas.append("SignelLinkedList{");
Node temp = getHead().next;
// 遍历出链表里面所有的内容
while (true) {
if (temp.next == null) {
datas.append(temp.data);
break;
}
datas.append(temp.data);
datas.append(", ");
temp = temp.next;
}
datas.append("}");
System.out.println(datas.toString());
}
}
3)编写测试类,对链表进行测试
public class BaseTest {
public static void main(String[] args) {
SignleLinkedList signleLinkedList = new SignleLinkedList();
signleLinkedList.add("张三");
signleLinkedList.add("李四");
signleLinkedList.add("王五");
System.out.println("======插入三条记录结果如下:");
signleLinkedList.findAll();
signleLinkedList.set(2, "赵六");
System.out.println("======将下标为2的记录改为【赵六】输出结果如下:");
signleLinkedList.findAll();
signleLinkedList.remove(1);
System.out.println("======删除下标为1的记录,结果如下:");
signleLinkedList.findAll();
}
}
执行结果如下图所示:
至此,最简单的单链表已经实现
五、单链表面试题
1)查找单链表中倒数第K个节点(新浪面试题)
思路:单链表中倒数的第K个节点,也就是该链表中正数第 (链表长度 - K)个节点,那么先算出该链表的长度,附上代码:
// 计算链表的长度
public int size() {
int linkedSize = 0;
if (HEADNODE.next == null) {
return 0;
}
Node temp = HEADNODE;
while (true) {
if (temp.next == null) {
break;
}
linkedSize++;
temp = temp.next;
}
return linkedSize;
}
然后该题也就可以理解为找出单链表中第 index 个节点。附上代码
// 找到单链表中的倒数第K个节点
public Node getNodeByIndexDesc(int k) {
Node node;
// 根据倒数第K个节点,算出该节点在链表中index
int index = this.size() - k;
if (index < 0 && index >= this.size()) {
throw new NullPointerException("此链表不存在倒数第" + k + "个元素");
} else {
int count = 0;//计数器
Node temp = this.getHead().next;
while (true) {
if (count == index) {
//数到第index的位置是,退出循环
break;
}
temp = temp.next; //找元素
count++;
}
node = temp;//最终找到的元素
}
return node;
}
好了,该面试题我们已经成功完成。
2)将单链表反转(腾讯面试题)
该题我们这里就暂时先不给出答案,感兴趣的小伙伴可以自己尝试完成这道题。(最好给自己限定一个时间,比如30分钟内完成)