一、顺序表:顺序表说白了就是对数组的规范使用
1.结构定义:
// 存储元素的数组
private T[] eles;
// 记录当前顺序表中元素个数
private int N;
// 构造方法
public MyOrderList(int capacity) {
// 初始化数组
this.eles = (T[])new Object[capacity];
// 初始化长度
this.N=0;
}
2.顺序表的各种操作
// 将每一个线性表置为空表
public void clear(){
this.N=0;
}
// 判断当前线性表是否为空表
public boolean isEmpty(){
return this.N==0;
}
// 获取线性表长度
public int Length(){
return this.N;
}
// 获取指定位置的元素
public T getFrom(int i){
return eles[i-1];
}
// 向线性表中添加元素
public void insertInto(T t){
if(N==eles.length){
resize(2*eles.length);
}
eles[this.N++]=t;
}
//在i元素处插入元素t(查询极端位置)
public void insertInto(int i,T t){
if(N==eles.length){
resize(2*eles.length);
}
i--;
//先把i索引处的元素及其以后的元素向后移动一位
for (int j = this.N; j >i; j--) {
eles[j]=eles[j-1];
}
//再把t元素放到i索引处即可
eles[i]=t;
this.N++;
}
//删除指定位置I处的元素并返回该元素
public T removeFrom(int i){
i--;
//记录索引i处的值
T target=eles[i];
//索引i后面的元素(不含索引i)依次向前移动一位
for (int j =i; j< (this.N-1); j++) {
eles[j]=eles[j+1];
}
//元素个数减一
this.N--;
if(N<eles.length/4){
resize(eles.length/2);
}
return target;
}
//查询t元素第一次出现的位置
public int indexOf(T t){
for (int i = 0; i < (this.N-1); i++) {
if(t.equals(eles[i])){
return i+1;
}
}
return -1;
}
//遍历顺序表
public void showOrderList(){
for (int i = 0; i < this.N ; i++) {
System.out.print(eles[i]+"\t");
}
}
//扩容和缩容方法:由newSize重置eles的大小
public void resize(int newSize){
//定义一个临时数组,指向原数组
T[] temp=eles;
//创建新数组
eles= (T[]) new Object[newSize];
//把原数组数据指向新数组
for (int i = 0; i < N; i++) {
eles[i]=temp[i];
}
}
3.以java特有方式实现对顺序表的遍历,首先自定义顺序表带有泛型实现Iterable接口,重写接口中的方法
@Override
public Iterator<T> iterator() {
return new SIterator();
}
private class SIterator implements Iterator{
private int cusor;
public SIterator() {
this.cusor=0;
}
@Override
public boolean hasNext() {
return cusor<N;
}
@Override
public Object next() {
return eles[cusor++];
}
}
顺序表是在计算机内存中以数组的形式保存的线性表,线性表的顺序存储是指用一组地址连续的存储单元依次存储线性表中的各个元素、使得线性表中在逻辑结构上相邻的数据元素存储在相邻的物理存储单元中,即通过数据元素物理存储的相邻关系来反映数据元素之间逻辑上的相邻关系,采用顺序存储结构的线性表通常称为顺序表。顺序表是将表中的结点依次存放在计算机内存中一组地址连续的存储单元中。(出自百度百科)
二、单链表
1.结构定义:
//记录头结点
private Node head;
//记录链表长度
private int N;
//结点类
private class Node{
//存储数据
T item;
//下一个结点
Node next;
public Node(T item,Node next){
this.item=item;
this.next=next;
}
}
public MyLinkedList() {
//初始化头结点
this.head=new Node(null,null);
//初始化元素个数
this.N=0;
}
2.对单链表的各种操作
//清空链表
public void clear(){
this.head.next=null;
this.N=0;
}
//获取链表长度
public int length(){
return this.N;
}
//判断链表是否为空
public boolean isEmpty(){
return this.N==0;
}
//获取指定位置i处的元素
public T get(int i){
//通过循环从头结点开始向后找i此即可得到目标
Node n=this.head.next;
for (int index = 0; index < i; index++) {
n=n.next;
}
return n.item;
}
//向链表中添加元素T
public void insert(T t){
//找到当前最后一个结点
Node n=this.head;
while (n.next!=null){
n=n.next;
}
//创建新结点,保存元素T
Node newNode = new Node(t, null);
//让当前最后一个结点指向新节点
n.next=newNode;
//元素个数+1
this.N++;
}
//向指定位置i处添加元素T
private void insert(int i,T t){
//找到i位置的前一个结点
Node pre=this.head;
for (int index = 0; index < i; index++) {
pre=pre.next;
}
//找到i位置结点
Node curr=pre.next;
//创建新结点,并且新结点需要指向原来i位置的结点
final Node newNode = new Node(t, curr);
//原来i位置的前一个结点指向新结点
pre.next=newNode;
//元素个数+1
this.N++;
}
//删除指定位置i处的元素,并返回被删除的元素
public T remove(int i){
//找到i位置的前一个结点
Node pre = this.head;
for (int index = 0; index < i; index++) {
pre=pre.next;
}
//找到i位置结点
Node nowNode = pre.next;
//找到i位置下一个结点
Node curr = nowNode.next;
//前一个结点指向下一个结点
pre.next=curr;
//元素个数减一
this.N--;
return nowNode.item;
}
//查找元素T在链表中第一次出现的位置
public int indexOf(T t){
//从头结点开始遍历,取出每一个item进行比较,若相同则找到了
Node n=head;
for (int i = 0; n.next!=null; i++) {
n=n.next;
if(n.item.equals(t)){
return i;
}
}
return -1;
}
3.以java特有方式实现对单链表的遍历,首先自定义单链表带有泛型实现Iterable接口,重写接口中的方法
@Override
public Iterator<T> iterator() {
return new LIterator();
}
private class LIterator implements Iterator{
private Node n;
public LIterator(){
this.n=head;
}
@Override
public boolean hasNext() {
return n.next!=null;
}
@Override
public Object next() {
n=n.next;
return n.item;
}
}
单链表是一种链式存取的数据结构,用一组地址任意的存储单元存放线性表中的数据元素。链表中的数据是以结点来表示的,每个结点的构成:元素(数据元素的映象) + 指针(指示后继元素存储位置),元素就是存储数据的存储单元,指针就是连接每个结点的地址数据。(出自百度百科)
三、双向链表
1.结构定义
//首结点
private Node head;
//最后一个结点
private Node last;
//链表的长度
private int N;
//节点类
private class Node{
public Node(T item,Node pre,Node next){
this.item=item;
this.pre=pre;
this.next=next;
}
//存储数据
public T item;
//指向上一个结点
public Node pre;
//指向下一个结点
public Node next;
}
public TwoWayLinkList() {
//初始化头结点和尾结点
this.head=new Node(null,null,null);
this.last=null;
// 初始化元素个数
this.N=0;
}
2.对双向链表的各种操作
//清空链表
public void clear(){
this.head.next=null;
this.head.pre=null;
this.head.item=null;
this.last=null;
this.N=0;
}
//获取链表长度
public int length(){
return N;
}
//判断链表是否为空
public Boolean isEmpty(){
return N==0;
}
//获取第一个元素
public T getFirst(){
if(isEmpty()){
return null;
}else {
return head.next.item;
}
}
//获取最后一个元素
public T getLast(){
if(isEmpty()){
return null;
}else {
return last.item;
}
}
//插入元素t
public void insert(T t){
if(isEmpty()){//若链表为空
//创建新结点
Node newNode = new Node(t, head, null);
//让新结点成为尾结点
last=newNode;
//头结点指向尾结点
head.next=last;
}else {
//若链表不为空
Node oldLast=last;
//创建新结点
Node newNode = new Node(t, oldLast, null);
//让当前尾结点指向新结点
oldLast.next=newNode;
//让新结点成为尾结点
last=newNode;
}
//元素个数+1
N++;
}
//向指定位置i处插入元素t
public void insert(int i,T t){
//找到i位置的前一个结点
Node pre=head;
//找到i位置的结点
for (int index = 0; index < i; index++) {
pre=pre.next;
}
Node curr = pre.next;
//创建新结点
Node newNode = new Node(t, pre, curr);
//让i位置的前一个结点的下一个结点变为新结点
pre.next=newNode;
//让i位置的前一个结点变为新结点
curr.pre=newNode;
//元素个数加一
N++;
}
//获取指定位置i处的元素
public T get(int i){
Node n=head.next;
for (int index = 0; index < i; index++) {
n=n.next;
}
return n.item;
}
//找到元素t在链表中第一次出现的位置
public int indexOf(T t){
Node n=head;
for(int i=0;n.next!=null;i++){
n=n.next;
if(n.next.equals(t)){
return i;
}
}
return -1;
}
//删除i位置的元素,并返回该元素
public T remove(int i){
//找到i位置的前一个结点
Node pre=head;
//找到i位置的前结点
for (int index = 0; index < i; index++) {
pre=pre.next;
}
//找到i位置的结点
Node curr = pre.next;
if((i+1)==N){
pre.next=null;
last=pre;
}else {
//找到i位置的下一个结点
Node nextNode = curr.next;
//让i位置的前一个结点的下一个结点变为i位置的下一个结点
pre.next=nextNode;
//让i位置的下一个结点的上一个结点变为i位置的前一个结点
nextNode.pre=pre;
}
//元素个数-1
N--;
return curr.item;
}
3.以java特有方式实现对双链表的遍历,首先自定义双链表带有泛型实现Iterable接口,重写接口中的方法
@Override
public Iterator<T> iterator() {
return new TIterator();
}
private class TIterator implements Iterator{
private Node n;
public TIterator() {
this.n=head;
}
@Override
public boolean hasNext() {
return n.next!=null;
}
@Override
public Object next() {
n=n.next;
return n.item;
}
}
双向链表也叫双链表,是链表的一种,它的每个数据结点中都有两个指针,分别指向直接后继和直接前驱。所以,从双向链表中的任意一个结点开始,都可以很方便地访问它的前驱结点和后继结点。一般我们都构造双向循环链表(出自百度百科)
四、栈
1.结构定义
//记录首结点
private Node head;
//栈中元素个数
private int N;
private class Node{
private T item;
private Node next;
public Node(T item, Node next) {
this.item = item;
this.next = next;
}
}
public MyStack() {
this.head=new Node(null,null);
this.N=0;
}
2.对栈的各种操作
//判断当前栈中元素个数是否为0
public boolean isEmpty(){return N==0;}
//获取栈中元素个数
public int size(){
return N;
}
//把t元素压入栈
public void push(T t){
//找到首结点指向的第一个结点
Node oldFirst = head.next;
//创建新结点
Node newNode = new Node(t, null);
//让首结点指向新结点
head.next=newNode;
//让新结点指向原来的第一个结点
newNode.next=oldFirst;
//元素个数+1
N++;
}
//弹出栈顶元素
public T pop(){
//找到首结点指向的第一个结点
Node oldFirst = head.next;
//让首结点指向原来第一个结点的下一个结点
if(oldFirst==null){
return null;
}
head.next=oldFirst.next;
//元素个数-1
N--;
return oldFirst.item;
}
3.以java特有方式实现对栈的遍历,首先自定义栈带有泛型实现Iterable接口,重写接口中的方法
@Override
public Iterator<T> iterator() {
return new SIterator();
}
private class SIterator implements Iterator{
private Node n;
public SIterator() {
this.n=head;
}
@Override
public boolean hasNext() {
return n.next!=null;
}
@Override
public Object next() {
n=n.next;
return n.item;
}
}
栈(stack)又名堆栈,它是一种运算受限的线性表。限定仅在表尾进行插入和删除操作的线性表。这一端被称为栈顶,相对地,把另一端称为栈底。向一个栈插入新元素又称作进栈、入栈或压栈,它是把新元素放到栈顶元素的上面,使之成为新的栈顶元素;从一个栈删除元素又称作出栈或退栈,它是把栈顶元素删除掉,使其相邻的元素成为新的栈顶元素。(出自百度百科)
五、队列
队列的结构定义
//队列的结构
private class Node{
public T item;
public Node next;
public Node(T item, Node next) {
this.item = item;
this.next = next;
}
}
//记录首结点
private Node head;
//记录最后一个结点
private Node last;
//记录队列中元素个数
private int N;
public MyQueue() {
this.head =new Node(null,null);
this.last=null;
this.N=0;
}
队列的各种操作
//判断队列是否为空
public boolean isEmpty(){
return N==0;
}
//返回队列中的元素个数
public int size(){
return N;
}
//向队列中插入元素t
public void enqueue(T t){
if(last==null){//当前尾结点last为null
last=new Node(t,null);
head.next=last;
}else {//当前尾结点last不为null
Node oldLast=last;
last=new Node(t,null);
oldLast.next=last;
}
//元素个数+1
this.N++;
}
//从队列中拿出一个元素
public T dequeue(){
if(isEmpty()){
return null;
}
Node oldFrist=head.next;
head.next=oldFrist.next;
N--;
//因为出队列是在删除元素,故若队列中的元素被删除完了需要重置last=null
if(isEmpty()){
last=null;
}
return oldFrist.item;
}
队列的遍历,在队列类实现Iterable<T>接口的条件下
@Override
public Iterator<T> iterator() {
return new QIterator();
}
private class QIterator implements Iterator{
private Node n;
public QIterator() {
this.n = head;
}
@Override
public boolean hasNext() {
return n.next!=null;
}
@Override
public Object next() {
n=n.next;
return n.item;
}
}
队列是一种特殊的线性表,特殊之处在于它只允许在表的前端(front)进行删除操作,而在表的后端(rear)进行插入操作,和栈一样,队列是一种操作受限制的线性表。进行插入操作的端称为队尾,进行删除操作的端称为队头。队列中没有元素时,称为空队列。队列的数据元素又称为队列元素。在队列中插入一个队列元素称为入队,从队列中删除一个队列元素称为出队。因为队列只允许在一端插入,在另一端删除,所以只有最早进入队列的元素才能最先从队列中删除,故队列又称为先进先出(FIFO—first in first out)线性表。(出自百度百科)
六、符号表(键值对)
结构定义
//定义内部类
private class Node{
//键
public Key key;
//值
public Value value;
//下一个结点
public Node next;
public Node(Key key, Value value, Node next) {
this.key = key;
this.value = value;
this.next = next;
}
}
//记录首结点
private Node head;
//记录符号中的元素个数
private int N;
public SymbolTable() {
this.head=new Node(null,null,null);
this.N=0;
}
对符号表的各种操作
//获取符号表中键值对的个数
public int size(){
return N;
}
//向符号表中插入键值对
public void put(Key key,Value value){
//符号表中已经存在键为key的键值对,则只需要将key的值进行覆盖
Node n=head;
while (n.next!=null){
//变换n
n=n.next;
//判断n结点存储的键你是否为n,如果是则覆盖其值
if(n.key.equals(key)){
n.value=value;
return;
}
}
//符号表中不存在key的键值对,直接存入即可
Node newNode = new Node(key, value, null);
Node oldFist = head.next;
newNode.next=oldFist;
head.next=newNode;
N++;
}
//删除符号表中键为key的键值对
public void delete(Key key){
//找到键为key的结点,把该结点删除
Node n = this.head;
while (n.next!=null){
//判断n结点的下一个结点是否为key
if(n.next.key.equals(key)){
n.next=n.next.next;
N--;
return;
}
n=n.next;
}
}
//从符号表中获取key对应的值
public Value get(Key key){
//找到键为key的结点
Node n = this.head;
while (n.next!=null){
n=n.next;
if(n.key.equals(key)){
return n.value;
}
}
return null;
}
有序符号表,有序符号表的实现不过是在无序符号表的基础上,在进行数据添加时将添加数据的键和已有的键进行大小比较,进而实现数据的有序。在原有的符号表的key实现了Comparable<Key>的基础上,对数据增加方法进行以下修改
//向符号表中插入键值对
public void put(Key key,Value value){
//定义两个结点分别记录当前结点和低昂前结点的上一个结点
Node curr = head.next;
Node pre = this.head;
while (curr!=null&&key.compareTo(curr.key)>0){
//变换当前结点和当前结点的上一个结点
pre=curr;
curr=curr.next;
}
//若要插入的键和当前的key值相同,则覆盖
if(curr!=null&& key.compareTo(curr.key)==0){
curr.value=value;
return;
}
//当前要插入的键和当前的key不同则插入
Node newNode = new Node(key, value, null);
newNode.next=curr;
pre.next=newNode;
N++;
}
符号表最主要的目的就是将一个键和一个值联系起来,符号表能够将存储的数据元素是一个键和一个值共同组成的。键值对数据,我们可以根据键来查找对应的值。
算法与数据结构排序与线性表篇小节:
快慢指针:
开门指针指移动速度有差别的两个指针,通过这个差别可以得到一个差值,长使用这个差值解决中值问题,单向链表是否成环问题,有环链表入口问题
题目分析:
1.在没有思路的时候,先找小数据做暴力穷举,然后看看能不能得出题目答案。
2.先用小数据看看是个什么数学公式。然后再翻译成代码
递归类计算:
容易出现的问题,递归要不断进栈。当存在大量数据时,只用递归使得栈内存溢出,同时也会消耗大量运算时间
解决方法,可以外置一个HashMap,通过HashMap保存每一次计算结果,这样可以减少栈内存溢出情况,同时也会节约大量运算时间
逆波兰表达式{
中缀表达式:a+b
后缀表达式:
a+b=ab+;a+(b-c)*d=abc-d*+
a*(b-c)+d=abc-*d+
}