Loading [MathJax]/jax/input/TeX/jax.js
前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >数据结构:树结构

数据结构:树结构

作者头像
ttony0
发布于 2022-12-26 10:43:34
发布于 2022-12-26 10:43:34
2K00
代码可运行
举报
运行总次数:0
代码可运行

普通树的结点树至少为1,不能为空;而二叉树可以为空

一、树的存储设计

1、双亲表示法(数组)

简单的数组储存,数组内容为:

adr

info

parent

0

A

-1

1

B

0

2

C

0

3

D

2

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
class Tree{
	   elemtype info;
    int par;
};

2、子女表示法(链表)

每个元素对应一个child链表,按顺序指向每一个孩子:

adr

info

child

0

A

->1->2

1

B

^

2

C

->3

3

D

^

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
class Tree{
	   elemtype info;
    node* child;
};

3、子女兄弟表示法

每个元素拥有两个指针,一个指向它的第一个孩子,另一个指向它的下一个兄弟:

FirstChild

info

NextSibling

B

A

^

^

B

C

D

C

^

^

D

^

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
class  TreeNode{
	   elemtype info;
    TreeNode *FirstChild,*NextSibling;
};

二、二叉树遍历

1、二叉树存储结构:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
class TreeNode{
	elemtype data;
  TreeNode *lchild,*rchild;
  TreeNode(elemtype D,TreeNode *lc=NULL,TreeNode *rc=NULL){
  	data=D;
  	lchild=lc;
  	rchild=rc;
  }
};

2、遍历:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
/*
前序遍历
*/
void PreTra(TreeNode *T){
    if(T==NULL)return;
    cout<<T->data;//此处对结点进行操作
    PreTra(T->lchild);
    PreTra(T->rchild);
    return;
}
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
/*
中序遍历
*/
void InTra(TreeNode *T){
    if(T==NULL)return;
    InTra(T->lchild);
    cout<<T->data;//此处对结点进行操作
    InTra(T->rchild);
    return;
}
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
/*
后序遍历
*/
void PosTra(TreeNode *T){
    if(T==NULL)return;
    PosTra(T->lchild);
    PosTra(T->rchild);
    cout<<T->data;//此处对结点进行操作
    return;
}

除了先序遍历、中序遍历、后序遍历外,还可以对二叉树进行层序遍历。层序遍历就是从所在二叉树的根节点出发,自上而下,自左至右逐层访问树的结点的过程。

层序遍历的实现需要利用队列结构,首先将根节点入队,当队列中有元素时,执行以下操作:将队首元素出队,对该元素进行操作,并将该元素的左子树、右子树依次入队。

层序遍历并不需要用到递归。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
/*
层序遍历
*/
void LevelTra(TreeNode *T){
    if(T==NULL)return;
    queue<TreeNode*> Q;
    Q.push(T);
    while(!Q.empty()){
    	TreeNode *S=Q.front();
    	Q.pop();
        cout<<S->data;//对元素进行操作
        if(S->lchild))Q.push(S->lchild);
        if(S->rchild))Q.push(S->rchild);
    }
    return;
}

三、线索二叉树

1、存储设计

线索二叉树的存储与普通二叉树类似,但是左指针、右指针多了标识符rtag和,ltag,当rtag为1时,rchild表示后继,当rtag为0时,rchild表示右子树,左标识符同理。总而言之,只利用二叉树的空指针表示线索

对一个确定的二叉树,分别有前序、中序、后序三种线索树,以下列二叉树为例:

它的前序遍历为ABDEC,则其前序线索树为:

2、线索化

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
/*
假设已经构建好二叉树T,构建中序线索树
*/
TreeNode *pre=NULL;//用于前驱线索的构建
void InitTheading(Tree *T){
		if(T){
				InitTheading(T->lchild);
				//L
				if(T->ltag==1) T->lchild=pre;//前驱
				if(pre && pre->rtag==1) pre->rchild=T;//后继
				pre=T;
				//N
				InitTheading(T->lchild);
				//R
		}
		return;
}

线索化代码需要注意的细节是前驱后继的处理,这里使用了全局变量pre存储当前操作结点的前驱,并以此得到结点T的前驱结点pre的后继

为了得到前序/后序线索树,只需要将上述代码的LNR交换位置。

3、任一结点前驱后继的查找

前序线索树来说,判断流程如下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
//前驱:
  if (p->ltag==1) pre=p->lchild;
  else //如图
//后继:
  if (p->rtag==1) next=p->rchild;
  else{
      if (p->ltag==0) next=p->lchild;
      else next=p->rchild;
  }

中序线索树来说,判断流程如下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
//前驱:
  if (p->ltag==1) pre=p->lchild;
  else {
  	pre=p->lchild;
  	while(pre->rchild){
  	     pre=pre->rchild;
      }
  }
//后继:
  if (p->rtag==1) next=p->rchild;
  else{
    next=p->rchild;
        while(next->lchild){
  	     next=next->lchild;
      }
  }

后序线索树来说,判断流程如下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
//前驱:
  if (p->ltag==1) pre=p->lchild;
  else {
  	if(p->rtag==0)pre=p->rchild;
  	else pre=p->lchild;
  }
//后继:
  if (p->rtag==1) next=p->rchild;
  else //如图

四、哈夫曼树

给定N个权值作为N个叶子结点,构造一棵二叉树,若该树的带权路径长度达到最小,称这样的二叉树为哈夫曼树(Huffman Tree)。哈夫曼树是带权路径长度最短的树,权值较大的结点离根较近。

哈夫曼树可用于编码,在编码时,让使用频率高的用短码,使用频率低的用长码,以优化整个编码。一个编码集合中,任何一个字符的编码都不是另一个字符编码的前缀,这种编码叫作前缀编码

其带权路径长度可以表示为WPL=nk=1wklk

1、存储设计

为了得到哈夫曼树,我们需要使用一种存储方式存储各个结点,为了便于算法计算,我们利用如下的结构作为结点:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
class TreeNode{
		int weight;
		int par;
		int lc,rc;
};

而且我们知道,当一棵哈夫曼树有N个叶结点时,它的结点总数为2N1,所以数组TreeNode arr[2*n-1]就是我们的哈夫曼树。

2、算法描述

假设我们得到了如下的叶结点,我们要一步一步构造哈夫曼树:

Adr

weight

par

lc

rc

0

6

-1

-1

-1

1

5

-1

-1

-1

2

8

-1

-1

-1

3

12

-1

-1

-1

4

0

-1

-1

-1

5

0

-1

-1

-1

6

0

-1

-1

-1

为了得到每一个结点,我们需要做如下步骤:

  • 找到当前已有结点(0~k)中无父结点中最小的两个结点A、B,令其父节点为第k+1个结点。
  • 第k+1个结点的权值为A与B的权值之和,令其左右子结点分别为A、B。
  • 更新已有结点个数:k+=1。
  • 将以上步骤循环n-1次,得到n-1个新结点,完成构造。

以上面为例,给出每一步的结果:

Adr

weight

par

lc

rc

0

6

4

-1

-1

1

5

4

-1

-1

2

8

-1

-1

-1

3

12

-1

-1

-1

4

11

-1

0

1

5

0

-1

-1

-1

6

0

-1

-1

-1

Adr

weight

par

lc

rc

0

6

4

-1

-1

1

5

4

-1

-1

2

8

5

-1

-1

3

12

-1

-1

-1

4

11

5

0

1

5

19

-1

2

4

6

0

-1

-1

-1

Adr

weight

par

lc

rc

0

6

4

-1

-1

1

5

4

-1

-1

2

8

5

-1

-1

3

12

6

-1

-1

4

11

5

0

1

5

19

6

2

4

6

31

-1

3

5

3、代码实现

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
#include <iostream>
#include <string>
#include <algorithm>

using namespace std;
 
class TreeNode{
	public:
	   int info,par,lc,rc;
	   TreeNode(){
        info=0; 
        par=lc=rc=-1;
	   }
};

void FindMin(int &min1,int &min2,int k,TreeNode *num){
//找到当前已有结点(0~k)中无父结点中最小的两个结点A、B
	for(int i=0;i<k;i++){
	   if(num[i].par==-1){
	       if(num[min1].info>num[i].info){
	           min1=i;
		  }
	   }
	}
	for(int i=0;i<k;i++){
	   if(num[i].par==-1 && i!=min1){
	       if(num[min2].info>num[i].info){
		      min2=i;
		  }
    }
	}
	return;
}

int main(){
    int n;
    cin>>n;
    TreeNode num[2*n-1];
    for(int i=0;i<n;i++)
    {
	       cin>>num[i].info;
    }
    num[2*n-1].info=1000;
    int min1=2*n-1,min2=2*n-1;
    for(int i=0;i<n-1;i++){
	//- 更新已有结点个数:k+=1,将以上步骤循环n-1次.
    FindMin(min1,min2,n+i,num);
	   num[min1].par=n+i;
	   num[min2].par=n+i;
	   num[n+i].info=num[min1].info+num[min2].info;
	   num[n+i].lc=min1;
	   num[n+i].rc=min2;
//第k+1个结点的权值为A与B的权值之和,令其左右子结点分别为A、B。
	   min1=min2=2*n-1;
	}
    for(int i=0;i<2*n-1;i++){
	       cout<<i<<"\t"<<num[i].info<<"\t"<<num[i].par<<"\t"<<num[i].lc<<"\t"<<num[i].rc<<endl;
    }
    return 0;
}

得到的输出和上面的表格相同

五、二叉树与森林的转化

每一棵树均可以转化为对应的二叉树,n棵树组成的森林同样可以组成一棵二叉树。

转化规则:

树使用子女兄弟表示法,每一个结点的子女视为左子树,兄弟视为右子树;若是多棵树组成的森林,注意到根结点一定没有兄弟,即没有右子树,可以将右子树连接到下一棵树的根结点,组成一棵二叉树。

根结点的右子树个数+1=森林中树的数量;

前序遍历一棵树等价于前序遍历该树对应的二叉树;

后序遍历一棵树等价于中序遍历该树对应的二叉树。

六、二叉查找树

二叉查找树(二叉排序树)或者是一棵空树,或者是具有下列性质的二叉树:

每个结点都有一个作为查找依据的关键字(key),所有结点的关键字互不相同。

左子树(若非空)上所有结点的关键字都小于根结点的关键字。

右子树(若非空)上所有结点的关键字都大于根结点的关键字。

左子树和右子树也是二叉查找树。

1、构造

新插入的值总作为叶子

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
void BST_insert(Node *T,Node *S )//结点S插入二叉查找树T中
{
    if(T == NULL)
    { 
        *T=*S;
        return;
    }
    Node *p,*q;
    int key = S->data;
    p = T;
    while(p)
    {
        q = p;
        if(p->data == key)
        {
            return;
        }
        else if(p->data > key){
            p = p->rchild;
        }
        else
        {
            p = p->lchild; 
        }
    }
    if(q->data < key) S = q->lchild;
    else S = q->rchild;
    return;
}

2、删除

删除二叉查找树的叶结点:直接删除即可;

删除二叉查找树的非叶结点

(1) 根结点有左右子树的情况下,选择根结点的左子树中的最大结点为新的根结点;或者是右子树中的最小结点为新的根结点;

(2)如果根结点没有左子树,则以右子树的根结点作为新的根结点;

(3)如果根结点没有右子树,则以左子树的根结点作为新的根结点。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
void BST_delete(Node *T,int key)
{
    if(!T)return;
    Node *p,*q;
    bool tag=0;//tag=0表示删除结点为左子树,tag=1表示删除结点为右子树
    p = T;
    while(p)
    {
        q=p;
        if(p->data == key)
        {
            break;
        }
        else if(p->data > key){
            p = p->rchild;
            tag = 1;
        }
        else
        {
            p = p->lchild; 
            tag = 0;
        }
    }
    if(p -> lchild == NULL && p->rchild == NULL)
    {
        //叶结点
        tag? q->rchild = NULL : q->lchild = NULL;
        delete p;
    }
    else 
    {
        if(p -> lchild == NULL){
            tag ? q->rchild = p->rchild : q->lchild = p->rchild;
            delete p;
            return;
        }
        else if(p -> rchild == NULL){
            tag ? q->rchild = p->lchild : q->lchild = p->lchild;
            delete p;
            return;
        }
        else 
        {
            Node *lc = p->lchild, *rc = p->rchild;
            delete p;
            p = lc;
            if(! p->rchild)
            {
                tag ? q->rchild = p : q->lchild = p;
                p->rchild = rc;
                return;
            }
            Node *r = p;
            while(p -> rchild)/找到左子树的最右子结点
            {
                r=p;
                p = p->rchild;
            }
            tag ? q->rchild = p : q->lchild = p;
            r->rchild = p->lchild; //注意将最右子节点的左子树连接到其父节点上
            p->rchild = rc;
            p->lchild = lc; 
            return;
        }
    }
}

3、查找

查找的过程在插入删除时已涉及到,注意查找不到时的返回值即可。

七、AVL树

在二叉查找树的基础上增加了一个变量:平衡因子=该结点右子树的高度-左子树的高度。

如果插入后平衡因子不满足1<=bal<=1

如果一棵二叉查找树是高度平衡的,它就成为AVL树。如果它有n个结点,其高度可保持在O(logn),平均查找长度也可保持在O(logn)。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
class Node{
	elemtype data;
	int bal;
  Node *lchild,*rchild;
  Node(elemtype D,TreeNode *lc=NULL,TreeNode *rc=NULL){
  	data=D;
  	lchild=lc;
  	rchild=rc;
  	bal = 0;
  }
};

插入一个新结点时,AVL树中相关结点的平衡状态会发生改变。因此,在插入一个新结点后,需要从插入位置沿通向根的路径回溯,检查各结点的平衡因子。(由于每个结点的平衡因子由其左右子树决定,插入路径外堆结点堆左右子树均不发生变化,所以平衡因子不会发生改变)

1、平衡化旋转

1)左单旋L

沿插入路径检查三个结点A、C和E。它们处于一条方向为“\”的直线上,需要做左单旋。旋转后更新的平衡因子为0。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
void RotateL(Node *p, Node *par, int tag)//p为结点A,par为其父结点,tag为par-p的连接方式
{
    Node *prc = p->rchild;//C
    p->rchild = prc->lchild;//A->D
    prc->lchild = p;
    //此时子树发生变化了的结点有A和C,只需要改变AC的平衡因子即可
    p->bal = prc->bal = 0;
    tag ? par->rchild = prc : par->lchild = prc;//更新par的子结点 
}

2)右单旋R

沿插入路径检查三个结点A、B和D。它们处于一条方向为“/”的直线上,需要做右单旋。旋转后更新的平衡因子为0。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
void RotateR(Node *p, Node *par, int tag)//p为结点A,par为其父结点,tag为par-p的连接方式
{
    Node *plc = p->lchild;//B
    p->rlhild = plc->rchild;//A->E
    plc->rchild = p;
    //此时子树发生变化了的结点有A和B,只需要改变AB的平衡因子即可
    p->bal = plc->bal = 0;
    tag ? par->rchild = prc : par->lchild = prc;//更新par的子结点 
}

3)左右双旋LR

从结点A起沿插入路径选取3个结点A、B和E,它们位于一条形如“<”的折线上,因此需要进行先左后右的双旋转。(双旋都是先旋转子结点,再旋转父结点,所以LR旋转构成一条<的折线,下面的RL旋转同理)

4)右左双旋RL

从结点A起沿插入路径选取3个结点A、C和D,它们位于一条形如“>”的折线上,需要进行先右后左的双旋转。

2、平衡因子更新

根据平衡树的定义,平衡树上所有结点的平衡因子的绝对值都不超过1。在插入结点之后,若查找树上某个结点的平衡因子的绝对值大于1,则说明出现不平衡。同时,失去平衡的最小子树的根结点必然离插入结点最近,其平衡因子的绝对值在插入之前大于零。

同时,插入的结点只能影响其祖先结点的平衡因子;

当某个平衡因子从0变成1或者-1,需要继续调整祖先结点的平衡因子,直到根节点;

当某个平衡因子从-1或者1变成0,则不需要调整祖先的平衡因子了,因为平衡因子在插入数据之后变成0,证明整棵树的高度没有发生变化;

当平衡因子在插入数据之后变成-2或者2,需要通过旋转来降低它的高度,使它继续保持AVL树的性质。

3、删除

当删除的结点不是叶结点,需要找到被删除结点的前驱/后继结点,将其填充进去,并删除该前驱/后继结点。

删除结点后需要调整平衡。

八、B树(B-树)

B树是一种平衡的多路搜索树, 结点最大的孩子数目称为B树的阶。一个m阶B树具有如下属性:

  • 树中每个结点至多有m棵子树;
  • 根结点至少有2棵子树;
  • 除根结点以外的所有非叶结点至少有m/2(向上取整)棵子树;
  • 所有非叶结点中包含下列信息数据 ( n, A0 , K1 , A1 , K2 , A2 , … , Kn, An ),其中 Ki(i=1,…,n)为关键字,且Ki < Ki+1 , Ai (i=0,…,n)为指向子结点的指针, n为关键字的个数;
  • 每个结点中的指针个数=关键字个数+1;
  • 所有的叶结点都位于同一层。事实上,每个结点中还应包含指向每个关键字的记录的指针。
  • 每个非叶结点的关键字个数都在[m/2-1, m-1]之间。

1、查找

B-树的查找过程是一个顺指针查找结点和在结点的关键字进行查找交叉进行的过程。因此,B-树的查找时间与B-树的阶数m和B-树的高度h直接有关。

在B-树上进行查找,查找成功所需的时间取决于关键字所在的层次查找不成功所需的时间取决于树的高度

2、插入

B树的建立是从空树开始,将关键字逐个插入形成。

插入在某个叶结点开始。如果在关键字插入后结点中的关键字个数超出了上界m-1,则结点需要“分裂”,否则可以直接插入。

分裂的规则是该结点分成两半,将中间的关键字进行提升加入到父结点中,但是这又可能存在父结点也满员的情况,则不得不向上进行回溯,甚至是要对根结点进行分裂,那么整棵树都加了一层。

在插入新关键字时,需要自底向上分裂结点,最坏情况下从被插关键码所在叶结点到根的路径上的所有结点都要分裂。

3、删除

在B-树上删除一个关键字时,首先需要找到这个关键字所在的结点,从中删去这个关键字。

删除非叶子结点必然会导致不满足B树性质。

若该结点不是叶结点,且被删关键字为 Ki,1<=i<=n,则在删去该关键字之后,应以该结点Ai (该关键字右侧)所指示子树中的最小关键字 x 来代替被删关键字 Ki 所在的位置,然后在 x 所在的叶结点中删除 x。删除之后会出现三种情况:

(1)被删关键字所在结点中的关键字个数>=[m/2],删除即可。

2)被删关键码所在结点中关键码个数n=[m/2]-1,说明删去该关键字后该结点将不满足B-树的定义,需要调整。

调整过程为:如果其左右兄弟结点中有“多余”的关键字,即与该结点相邻的右(左)兄弟结点中的关键字数目大于[m/2]-1。则可将右(左)兄弟结点中最小(大)关键字上移至双亲结点。而将双亲结点中小(大)于该上移关键字的最大(小)关键字下移至被删 关键字所在结点中。

(3)被删关键码所在结点和其相邻的左右兄弟节点中的关键码个数均等于[m/2]-1,左右兄弟都不够借。

需要把要删除关键字的结点与其左(或右)兄弟结点以及双亲结点中分割二者的关键字合并成一个结点,假设其有右兄弟结点,且其右兄弟结点是由双亲结点中的指针 Ai 所指,则需要在删除该关键字的同时,将剩余的关键字和指针连同双亲结点中的 Ki 一起合并到右兄弟结点中。

删除53:

删除12、37:

九、B+树

树小结

我的博客即将同步至腾讯云开发者社区,邀请大家一同入驻:https://cloud.tencent.com/developer/support-plan?invite_code=1kccxzkuz8480


本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2019-12-29 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
浅谈切比雪夫多项式推导及其实现模版归类
切比雪夫多项式 概述: 切比雪夫多项式是与棣美弗定理有关,以递归方式定义的一系列正交多项式序列。 通常,第一类切比雪夫多项式以符号Tn表示, 第二类切比雪夫多项式用Un表示。切比雪夫多项式 Tn 或 Un 代表 n 阶多项式。 切比雪夫多项式在逼近理论中有重要的应用。这是因为第一类切比雪夫多项式的根(被称为切比雪夫节点)可以用于多项式插值。相应的插值多项式能最大限度地降低龙格现象,并且提供多项式在连续函数的最佳一致逼近。 基本性质: 对每个非负整数n, Tn(x) 和 Un(x) 都为 n次多项式。 并且当
Angel_Kitty
2018/04/10
3.1K0
浅谈切比雪夫多项式推导及其实现模版归类
PAT (Basic Level) Practice (中文)1051 复数乘法 (15 分)
复数可以写成 (A+Bi) 的常规形式,其中 A 是实部,B 是虚部,i 是虚数单位,满足 i​2​​=−1;也可以写成极坐标下的指数形式 (R×e​(Pi)​​),其中 R 是复数模,P 是辐角,i 是虚数单位,其等价于三角形式 R(cos(P)+isin(P))。
glm233
2020/09/28
3320
PAT(乙级)1051.复数乘法(15)
复数可以写成 (A+Bi) 的常规形式,其中 A 是实部,B 是虚部,i 是虚数单位,满足 i^2=−1;也可以写成极坐标下的指数形式 (R×e​(Pi)),其中 R 是复数模,P 是辐角,i是虚数单位,其等价于三角形式 R(cos(P)+isin(P))。 现给定两个复数的 R 和 P,要求输出两数乘积的常规形式。 输入格式: 输入在一行中依次给出两个复数的 R​1, P​1 , R​2​​ , P​2 ,数字间以空格分隔。
lexingsen
2022/02/25
3160
1051. 复数乘法 (15)
复数可以写成(A + Bi)的常规形式,其中A是实部,B是虚部,i是虚数单位,满足i2 = -1;也可以写成极坐标下的指数形式(R*e(Pi)),其中R是复数模,P是辐角,i是虚数单位,其等价于三角形式 R(cos(P) + isin(P))。
AI那点小事
2020/04/20
2280
1051. 复数乘法 (15)
1051 复数乘法 (15 分)
复数可以写成 (A+Bi) 的常规形式,其中 A 是实部,B 是虚部,i 是虚数单位,满足 i2=−1;也可以写成极坐标下的指数形式 (R×e(Pi)),其中 R 是复数模,P 是辐角,i 是虚数单位,其等价于三角形式 R(cos(P)+isin(P))。
可爱见见
2019/10/15
4550
CORDIC算法详解(三)- CORDIC 算法之线性系统及其数学应用
网上有很多类似的介绍,但是本文会结合实例进行介绍,尽量以最简单的语言进行解析。   CORDIC ( Coordinate Rotation Digital Computer ) 是坐标旋转数字计算机算法的简称, 由 Vloder• 于 1959 年在设计美国航空导航控制系统的过程中首先提出[1], 主要用于解决导航系统中三角函数、 反三角函数和开方等运算的实时计算问题。 1971 年, Walther 将圆周系统、 线性系统和双曲系统统一到一个 CORDIC 迭代方程里 , 从而提出了一种统一的CORDIC 算法形式[2]。   CORDIC 算法应用广泛, 如离散傅里叶变换 、 离散余弦变换、 离散 Hartley 变换、Chirp-Z 变换、 各种滤波以及矩阵的奇异值分解中都可应用 CORDIC 算法。 从广义上讲,CORDIC 算法提供了一种数学计算的逼近方法。 由于它最终可分解为一系列的加减和移位操作, 故非常适合硬件实现。 例如, 在工程领域可采用 CORDIC 算法实现直接数字频率合成器。 本节在阐述 CORDIC 算法三种旋转模式的基础上, 介绍了利用 CORDIC 算法计算三角函数、 反三角函数和复数求模等相关理论。 以此为依据, 阐述了基于 FPGA 的 CORDIC 算法的设计与实现及其工程应用。
碎碎思
2020/06/28
2K0
1051 复数乘法 (15 分)
版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
韩旭051
2019/11/08
4340
matlab数据类型 —— 复型(复数)
等等复数开偶次方的情况无法计算,为了使这种情况有解,便将数集扩充,便有了复数集。
繁依Fanyi
2023/05/07
1.3K0
matlab数据类型 —— 复型(复数)
python complex函数
>>> c=complex(3,4) >>> d=complex(2,5) >>> c*d (-14+23j)
用户7886150
2021/01/23
6270
通俗理解LDA主题模型
0 前言 印象中,最开始听说“LDA”这个名词,是缘于rickjin在2013年3月写的一个LDA科普系列,叫LDA数学八卦,我当时一直想看来着,记得还打印过一次,但不知是因为这篇文档的前序铺垫太长(现在才意识到这些“铺垫”都是深刻理解LDA 的基础,但如果没有人帮助初学者提纲挈领、把握主次、理清思路,则很容易陷入LDA的细枝末节之中),还是因为其中的数学推导细节太多,导致一直没有完整看完过。 理解LDA,可以分为下述5个步骤: 一个函数:gamma函数 四个分布:二项分布、多项分布、beta分布、Dir
机器学习AI算法工程
2018/03/13
20.8K0
通俗理解LDA主题模型
Sinusoidal 位置编码追根溯源
这篇文章主要是介绍自己对 Google 在《Attention is All You Need》中提出来的 Sinusoidal 位置编码
mathor
2021/05/13
1.5K0
Sinusoidal 位置编码追根溯源
计算机搞定44年几何难题,原来这2个人25年前猜对了
最近,来自美国、加拿大、瑞士的4位数学家,用C++和MATLAB程序解出了一个6元105项方程的59组特殊解。
量子位
2021/02/26
5690
计算机搞定44年几何难题,原来这2个人25年前猜对了
复数乘法 C语言
复数可以写成 (A+Bi) 的常规形式,其中 A 是实部,B 是虚部,i 是虚数单位,满足 i2=−1;也可以写成极坐标下的指数形式 (R×e(Pi)),其中 R 是复数模,P 是辐角,i 是虚数单位,其等价于三角形式 R(cos(P)+isin(P))。
叶茂林
2023/07/28
2970
Matlab系列之那些数学函数(讨论功能已加入)
本来是打算写关于矩阵的一些东西,但是弄了一半,发现需要的线代知识有点多,直接讲相关的使用,就太直白了,可能根本无法理解是什么意思,如果讲线代的知识,就感觉和该系列的文不太符,所以直接弃了那部分,打算之后讲到其他记录的时候,夹杂在其中进行,本篇就对MATLAB中常用的数学函数做一些记录。
狂人V
2020/09/23
1.1K0
Matlab系列之那些数学函数(讨论功能已加入)
【愚公系列】2023年08月 3D数学-三角函数
在数学中,函数是一种映射关系,它将一个集合中的元素映射到另一个集合中的元素。通常来说,一个函数由三个部分组成:输入、输出和规则。输入是指函数接收的变量或数值,输出是指函数根据规则计算后得到的结果,规则则是描述输入与输出之间关系的公式或算法。
愚公搬代码
2025/05/28
1010
【愚公系列】2023年08月 3D数学-三角函数
一文看懂 LLaMA 中的旋转式位置编码(Rotary Position Embedding)
旋转式位置编码(RoPE)最早是论文[1]提出的一种能够将相对位置信息依赖集成到 self-attention 中并提升 transformer 架构性能的位置编码方式。而目前很火的 LLaMA 模型也是采用该位置编码方式。
BBuf
2023/08/22
5.6K0
一文看懂 LLaMA 中的旋转式位置编码(Rotary Position Embedding)
FFmpeg + OpenGL ES 实现 3D 全景播放器
前文中,我们已经利用 FFmpeg + OpenGLES + OpenSLES 实现了一个多媒体播放器,本文将基于此播放器实现一个酷炫的 3D 全景播放器。
字节流动
2020/09/14
1.4K0
点积与叉积
如果a和b都是单位向量,那么点积的结果就是其夹角的cos值。
全栈程序员站长
2022/11/17
1.3K0
点积与叉积
欧拉公式
欧拉公式、麦克斯韦方程组、牛顿第二定律、勾股定理、薛定谔方程、质能方程、德布罗意方程组、1+1=2、傅立叶变换、圆的周长公式。
小小杨
2021/10/13
3.5K0
武忠祥老师每日一题|第320 - 335题
代入 初值: -1 = f(0) = -1 + C C = 0 f(x) = x - 1
一只野生彩色铅笔
2022/09/20
2.2K0
推荐阅读
相关推荐
浅谈切比雪夫多项式推导及其实现模版归类
更多 >
LV.0
这个人很懒,什么都没有留下~
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
本文部分代码块支持一键运行,欢迎体验
本文部分代码块支持一键运行,欢迎体验