内容简介:This post is going to cover normalizing allThis automation normalizes all volume levels in my home based on the time of day using theThis is one of my favorite automations in my house and I think you will enjoy it too! I wrote this automation for several r
This post is going to cover normalizing all media_player
volume levels with Home Assistant
and Node-RED
.
If you haven’t already, I’d recommend reading my previous blog post on sending Text-to-Speech notifications
which
dives a bit deeper on the optional accessibility Subflow this Flow consumes.
This automation normalizes all volume levels in my home based on the time of day using the Amazon Alexa Media Player Integration to bring in all my Amazon Echo and ecobee Switch+ devices into Home Assistant.
This is one of my favorite automations in my house and I think you will enjoy it too! I wrote this automation for several reasons:
- I wanted all speakers to be quiet at bed time. This prevents someone from getting blasted at 2:00 am in the morning.
- In the morning after I’ve had my cup of coffee, I want to increase the volume to an acceptable level.
- Automation allows for reuse via sub-flows. This means you can trigger it for other use cases like friends are over and music is playing, increase or decrease the volume.
- One less thing I have to think about, or constantly touch to change. In todays pandemic, this is also one less thing you potentially have to clean!
- You can use this same routine for increasing the volume when triggering an alarm.
- All speakers are at the same volume level when playing music on every speaker.
This Automation Flow is comprised of two different Subflows .
Alexa Media Player Custom Component
If you haven’t done so already, install the Alexa Media Player Custom Component if you are using Alexa, otherwise skip this section.
This addon is amazing as it normalizes all of your Alexa enabled devices such
as the Amazon Echo
or ecobee Switch+
into a media_player
that can be used by Home Assistant.
Service to set volume level.
This Automation Flow controls the volume levels by calling the media_player.volume_set
service. I’d recommend testing it first by opening
the Home Assistant Developer tools and navigating to the Services tab.
Next, start playing some music on your media_player
and call the media_player.volume_set
service with a payload of:
entity_id: media_player.amazon_echo_plus volume_level: 0.6
If you don’t hear a volume change, try a different volume_level
in case the
the device is already at that level. If it’s not then stop and check the logs.
This will help diagnose why the device isn’t working and save you lots of time
before continuing.
Input Boolean to control automations
I highly recommend adding multipleInput Booleansthat controls if a specific automation or all automations can run. It’s always
nice to be able to turn on or off a specific automation or all in the case of
maintenance. Here are the three input_boolean
s that are used in this
Automation Flow.
If you choose a different name, be sure to update the code below.
automation_enable: name: Enable Automations icon: mdi:home-automation automation_normalize_volume: name: Automation - Automatically Normalize Volume throughout the day icon: mdi:volume-high automation_notifications: name: Notify when an automation is triggered icon: mdi:home-automation
Set Volume Level Subflow
This Subflow will get the current devices volume level, compare the current and desired state. If changes are required, it will call the required service to change the devices volume level.
I then create a new Subflow with a status node, one input and one output. This
Subflow optionally depends on the
Send Automation Speech Notification Subflow
.
graph TD InputNode(Input) --> ACurrentStateNode(fas:fa-database Get Current Volume Level) ACurrentStateNode --> BFunctionNode(fas:fa-code Set Volume Level Payload) BFunctionNode --> CCallServiceNode(fas:fa-play Set Volume Level) BFunctionNode --> DCallSubflowNode(fas:fa-sliders-h Send Automation Speech Notification) CCallServiceNode --> OutputNode(Output 1) StatusNode(fas:fa-heartbeat Status: All) --> StatusOutputNode(Status) style StatusNode fill:#ECF5FF style StatusOutputNode fill:#FAFAFB style InputNode fill:#FAFAFB style ACurrentStateNode fill:#66ACFD style BFunctionNode fill:#FBB68F style CCallServiceNode fill:#66ACFD style DCallSubflowNode fill:#FF75A1 style OutputNode fill:#FAFAFB linkStyle default stroke-width:2px,fill:none,stroke:#CCD0D4
Let’s break down the Subflow. The incoming message gets passed to the Get Current Volume Level
Current State node to populate msg.data
with the
entity. This is then passed to the Set Volume Level Payload
node which checks
to see if any changes are required to meet the desired volume level and outputs
Text-to-Speech (TTS) status messages. If changes are required, the Set Volume Level
node will call the media_player.volume_set
service.
Here is the JavaScript code contained in the Set Volume Level Payload
Function node to smartly adjust the volume only if it is needed.
const entity = msg.payload && msg.payload.entity_id; const attributes = msg.data && msg.data.attributes if (!entity || !attributes) { node.status({ fill: "red", shape: "dot", text: "Invalid volume payload" }); return [null, null]; } const desired_volume_level = msg.payload.volume_level || 0; if (desired_volume_level < 0.0 || desired_volume_level > 1.0) { let message = "Volume level must be between 0 and 1."; node.status({ fill: "red", shape: "dot", text: message }); node.error(message); return [null, { payload: { entity_id: entity, message: message } }]; } if (desired_volume_level === attributes.volume_level) { let message = "Volume level is already at the desired level."; node.status({ fill: "grey", shape: "dot", text: message }); node.log(message); return [null, { payload: { entity_id: entity, message: message } }]; } const volumePayload = { payload: { data: { entity_id: entity, volume_level: desired_volume_level } } }; let message = "Setting Volume Level to " + (desired_volume_level * 100) + " percent."; const speechPayload = { payload: { entity_id: entity, message: message } }; node.status({ fill: "green", shape: "dot", text: message }); return [volumePayload, speechPayload];
As you an see it will output a friendly speech notification payload that will
be sent to the Send Automation Speech Notification
Subflow. If you are not
using the alexa_media_player
Home Assistant addon, you may want to update the
you’ll want to update Set Volume Level Payload
Function node to take in a
volume range you are expecting.
You can trigger this Subflow by passing a message object with the following payload. I recommend using a Inject Node to test this out.
{ "entity_id": "media_player.amazon_echo_plus", "volume_level":0.4 }
Please note, for my Alexa speakers. I use a volume level between the range of [0.0..0.10)
. To mute, pass 0.0
.
Set Volume Level on All Media Devices Subflow
This subflow will get all media_player
’ and then send them one by one to the Set Volume Level
Subflow.
If you are not using Alexa, you may need to tweak the Get All Media Players
node as it’s returning all devices that have a specific feature attribute
which says I support changing the volume level.
graph TD InputNode(Input) --> AFunctionNode(fas:fa-code Parse Volume Level) AFunctionNode --> BGetEntitiesNode(fas:fa-search Get All Media Players) BGetEntitiesNode --> CChangeNode(fas:fa-random Set Volume Level Payload) CChangeNode --> DSubflowNode(fas:fa-sliders-h Set Volume Level) DSubflowNode --> OutputNode(Output) StatusNode(fas:fa-heartbeat Status: All) --> StatusOutputNode(Status) style StatusNode fill:#ECF5FF style StatusOutputNode fill:#FAFAFB style InputNode fill:#FAFAFB style AFunctionNode fill:#FBB68F style BGetEntitiesNode fill:#66ACFD style CChangeNode fill:#FEA530 style DSubflowNode fill:#FF75A1 style OutputNode fill:#FAFAFB linkStyle default stroke-width:2px,fill:none,stroke:#CCD0D4
You can trigger this Subflow by passing a message object with the
following float
(e.g. 0.1
) payload. I recommend using a Inject Node
to test this out.
Normalize Volume Levels Everywhere Flow
This flow brings the two previous sub flows together to normalize all volume levels based on the time of the day. In my home, I set the level to 30% from 9:00am to 9:00pm, otherwise I set to 10%.
To have the flow set the volume levels at different times of the day, I use the BigTimer Node
with an On Time
of 09:00
and a Off Time
of 21:00
. Then I set the ON Msg
to .3
and a OFF Msg
to .1
. When the timer turns on and turns off,
it will check to see if my automation flags are turned on. If they are, it will
enumerate over all media devices and set the volume based on the timers output
message (e.g., .1
or .3
).
graph TD ABigTimerNode(fas:fa-clock Normalize Volume) --> BCurrentStateNode(fas:fa-database Automations Enabled?) BCurrentStateNode --> CCurrentStateNode(fas:fa-database Normalize Volume Levels?) CCurrentStateNode --> DRbeNode(fas:fa-scroll Only allow changed values) DRbeNode --> ECallSubflow(fas:fa-sliders-h Set Volume Level on All Media Devices Subflow) StatusNode(fas:fa-heartbeat Status: All) --> StatusOutputNode(Status) style StatusNode fill:#ECF5FF style StatusOutputNode fill:#FAFAFB style ABigTimerNode fill:#3DB39F style BCurrentStateNode fill:#66ACFD style CCurrentStateNode fill:#66ACFD style DRbeNode fill:#FEB95E style ECallSubflow fill:#FF75A1 linkStyle default stroke-width:2px,fill:none,stroke:#CCD0D4
You can import this Flow and all Subflows shown above by importing the following JSON.
[ { "id": "7a62a2d3.c0f4a4", "type": "Subflow", "name": "Send Automation Speech Notification", "info": "", "category": "", "in": [ { "x": 60, "y": 100, "wires": [ { "id": "87411254.a6ed18" } ] } ], "out": [ { "x": 1120, "y": 100, "wires": [ { "id": "d44cc51a.be0668", "port": 0 } ] } ], "status": { "x": 220, "y": 40, "wires": [ { "id": "aef53056.742438", "port": 0 } ] } }, { "id": "89300b1.595bcf8", "type": "function", "z": "7a62a2d3.c0f4a4", "name": "Set Speech Payload", "func": "const entity = flow.get(\"$parent.speech_entity_id\") || (msg.payload && msg.payload.entity_id) || \"media_player.office_echo_plus\";\nconst message = (msg.payload && msg.payload.message) || \"Automation provided no message\";\nconst announcement = msg.payload && msg.payload.announcement;\n\nif (announcement) {\n node.status({ fill: \"green\", shape: \"dot\", text: \"Announce message:\" + message });\n return {\n payload:{\n data: {\n message: message,\n data: { \"type\": \"announce\", \"method\": \"all\" },\n target: !!entity ? [entity] : []\n }\n }\n };\n}\n\nnode.status({ fill: \"green\", shape: \"dot\", text: \"TTS message:\" + message });\nreturn {\n payload:{\n data: {\n message: message,\n data: { type: \"tts\" },\n target: [entity]\n }\n }\n};", "outputs": 1, "noerr": 0, "x": 700, "y": 100, "wires": [ [ "d44cc51a.be0668" ] ] }, { "id": "d44cc51a.be0668", "type": "api-call-service", "z": "7a62a2d3.c0f4a4", "name": "Send Speech Notification", "server": "61956bd4.93df44", "version": 1, "debugenabled": false, "service_domain": "notify", "service": "alexa_media", "entityId": "", "data": "", "dataType": "json", "mergecontext": "", "output_location": "payload", "output_location_type": "msg", "mustacheAltTags": false, "x": 950, "y": 100, "wires": [ [] ] }, { "id": "6add9bc6.4c3624", "type": "api-current-state", "z": "7a62a2d3.c0f4a4", "name": "Speech Notifications?", "server": "61956bd4.93df44", "version": 1, "outputs": 2, "halt_if": "true", "halt_if_type": "bool", "halt_if_compare": "is", "override_topic": false, "entity_id": "input_boolean.automation_notifications", "state_type": "habool", "state_location": "", "override_payload": "none", "entity_location": "", "override_data": "none", "blockInputOverrides": false, "x": 440, "y": 140, "wires": [ [ "89300b1.595bcf8" ], [] ], "outputLabels": [ "", "enabled" ] }, { "id": "aef53056.742438", "type": "status", "z": "7a62a2d3.c0f4a4", "name": "", "scope": null, "x": 100, "y": 40, "wires": [ [] ] }, { "id": "87411254.a6ed18", "type": "function", "z": "7a62a2d3.c0f4a4", "name": "Check for overrides", "func": "const alwaysSpeak = msg.payload && msg.payload.announcement\nif (alwaysSpeak) {\n return [msg, null];\n} else {\n return [null, msg];\n}", "outputs": 2, "noerr": 0, "x": 210, "y": 100, "wires": [ [ "89300b1.595bcf8" ], [ "6add9bc6.4c3624" ] ], "outputLabels": [ "Bypass notification", "Check for notifications" ] }, { "id": "974adda.d2ea92", "type": "Subflow", "name": "Set Volume Level", "info": "Set Volume Level", "category": "", "in": [ { "x": 80, "y": 120, "wires": [ { "id": "4c86fd47.0875e4" } ] } ], "out": [ { "x": 960, "y": 100, "wires": [ { "id": "263d05a1.5b6672", "port": 0 } ] } ], "status": { "x": 240, "y": 40, "wires": [ { "id": "7a662cd5.162a24", "port": 0 } ] } }, { "id": "563c252e.ce31e4", "type": "function", "z": "974adda.d2ea92", "name": "Set Volume Level Payload", "func": "const entity = msg.payload && msg.payload.entity_id;\nconst attributes = msg.data && msg.data.attributes\nif (!entity || !attributes) {\n node.status({ fill: \"red\", shape: \"dot\", text: \"Invalid volume payload\" });\n return [null, null];\n}\n\nconst desired_volume_level = msg.payload.volume_level || 0;\nif (desired_volume_level < 0.0 || desired_volume_level > 1.0) {\n let message = \"Volume level must be between 0 and 1.\"; \n node.status({ fill: \"red\", shape: \"dot\", text: message });\n node.error(message);\n \n return [null, { \n payload: {\n entity_id: entity,\n message: message\n }\n }];\n}\n\nif (desired_volume_level === attributes.volume_level) {\n let message = \"Volume level is already at the desired level.\"; \n node.status({ fill: \"grey\", shape: \"dot\", text: message });\n node.log(message);\n return [null, { \n payload: {\n entity_id: entity,\n message: message\n }\n }];\n}\n\nconst volumePayload = { \n payload: {\n data: {\n entity_id: entity,\n volume_level: desired_volume_level\n } \n }\n};\n\nlet message = \"Setting Volume Level to \" + (desired_volume_level * 100) + \" percent.\";\nconst speechPayload = { \n payload: {\n entity_id: entity,\n message: message\n }\n};\n\nnode.status({ fill: \"green\", shape: \"dot\", text: message });\nreturn [volumePayload, speechPayload];", "outputs": 2, "noerr": 0, "x": 510, "y": 120, "wires": [ [ "263d05a1.5b6672" ], [ "b41308c0.f41e9" ] ], "inputLabels": [ "Volume Percentage" ], "outputLabels": [ "Volume Level Payload", "Speech Notification Payload" ] }, { "id": "263d05a1.5b6672", "type": "api-call-service", "z": "974adda.d2ea92", "name": "Set Volume Level", "server": "61956bd4.93df44", "version": 1, "debugenabled": false, "service_domain": "media_player", "service": "volume_set", "entityId": "", "data": "", "dataType": "json", "mergecontext": "", "output_location": "payload", "output_location_type": "msg", "mustacheAltTags": false, "x": 770, "y": 100, "wires": [ [] ] }, { "id": "b41308c0.f41e9", "type": "Subflow:7a62a2d3.c0f4a4", "z": "974adda.d2ea92", "name": "", "env": [], "x": 830, "y": 160, "wires": [ [] ] }, { "id": "4c86fd47.0875e4", "type": "api-current-state", "z": "974adda.d2ea92", "name": "Get Current Volume Level", "server": "61956bd4.93df44", "version": 1, "outputs": 1, "halt_if": "", "halt_if_type": "str", "halt_if_compare": "is", "override_topic": true, "entity_id": "", "state_type": "str", "state_location": "", "override_payload": "none", "entity_location": "data", "override_data": "msg", "blockInputOverrides": false, "x": 250, "y": 120, "wires": [ [ "563c252e.ce31e4" ] ] }, { "id": "7a662cd5.162a24", "type": "status", "z": "974adda.d2ea92", "name": "", "scope": null, "x": 120, "y": 40, "wires": [ [] ] }, { "id": "76be143e.dbb0e4", "type": "Subflow", "name": "Set Volume Level on All Media Devices", "info": "", "category": "", "in": [ { "x": 100, "y": 120, "wires": [ { "id": "642c5f33.bd92a8" } ] } ], "out": [ { "x": 1120, "y": 120, "wires": [ { "id": "15d5ddae.8f64c2", "port": 0 } ] } ], "env": [], "color": "#DDAA99", "status": { "x": 1120, "y": 40, "wires": [ { "id": "ba88f8d5.b5d648", "port": 0 }, { "id": "15d5ddae.8f64c2", "port": 0 } ] } }, { "id": "f467a711.24ca3", "type": "ha-get-entities", "z": "76be143e.dbb0e4", "server": "61956bd4.93df44", "name": "Get All Media Players", "rules": [ { "property": "attributes.supported_features", "logic": "is", "value": "56253", "valueType": "num" }, { "property": "attributes.available", "logic": "is", "value": "true", "valueType": "bool" } ], "output_type": "split", "output_empty_results": true, "output_location_type": "msg", "output_location": "payload", "output_results_count": 1, "x": 480, "y": 120, "wires": [ [ "1fe46c89.0e0543" ] ] }, { "id": "1fe46c89.0e0543", "type": "change", "z": "76be143e.dbb0e4", "name": "Set Volume Level Payload", "rules": [ { "t": "set", "p": "payload.volume_level", "pt": "msg", "to": "volume_level", "tot": "flow" } ], "action": "", "property": "", "from": "", "to": "", "reg": false, "x": 730, "y": 120, "wires": [ [ "15d5ddae.8f64c2" ] ] }, { "id": "642c5f33.bd92a8", "type": "function", "z": "76be143e.dbb0e4", "name": "Parse Volume Level", "func": "const volumeLevel = parseFloat(msg.payload);\nnode.status({ fill: \"green\", shape: \"dot\", text: \"Volume Level: \" + volumeLevel });\nflow.set(\"volume_level\", volumeLevel);\nreturn msg;", "outputs": 1, "noerr": 0, "x": 250, "y": 120, "wires": [ [ "f467a711.24ca3" ] ] }, { "id": "15d5ddae.8f64c2", "type": "Subflow:974adda.d2ea92", "z": "76be143e.dbb0e4", "name": "", "env": [], "x": 970, "y": 120, "wires": [ [] ] }, { "id": "ba88f8d5.b5d648", "type": "status", "z": "76be143e.dbb0e4", "name": "", "scope": null, "x": 140, "y": 40, "wires": [ [] ] }, { "id": "3c07a459.ce903c", "type": "tab", "label": "Normalize Volume", "disabled": false, "info": "Normalize All Volume" }, { "id": "e949c86f.c9ca9", "type": "api-current-state", "z": "3c07a459.ce903c", "name": "Automations Enabled?", "server": "61956bd4.93df44", "version": 1, "outputs": 2, "halt_if": "true", "halt_if_type": "bool", "halt_if_compare": "is", "override_topic": false, "entity_id": "input_boolean.automation_enable", "state_type": "habool", "state_location": "", "override_payload": "none", "entity_location": "", "override_data": "none", "blockInputOverrides": false, "x": 560, "y": 100, "wires": [ [ "ec5308f5.aefff" ], [] ], "outputLabels": [ "", "enabled" ] }, { "id": "ec5308f5.aefff", "type": "api-current-state", "z": "3c07a459.ce903c", "name": "Normalize Volume Levels?", "server": "61956bd4.93df44", "version": 1, "outputs": 2, "halt_if": "true", "halt_if_type": "bool", "halt_if_compare": "is", "override_topic": false, "entity_id": "input_boolean.automation_normalize_volume", "state_type": "habool", "state_location": "", "override_payload": "none", "entity_location": "", "override_data": "none", "blockInputOverrides": false, "x": 810, "y": 100, "wires": [ [ "bde48ad8.814ac" ], [] ], "outputLabels": [ "", "enabled" ] }, { "id": "dbf47096.3a7dc", "type": "inject", "z": "3c07a459.ce903c", "name": "", "topic": "", "payload": "on", "payloadType": "str", "repeat": "", "crontab": "", "once": false, "onceDelay": 0.1, "x": 110, "y": 80, "wires": [ [ "4608374e.2fd4d8" ] ] }, { "id": "6eace9df.a5aa68", "type": "inject", "z": "3c07a459.ce903c", "name": "", "topic": "", "payload": "off", "payloadType": "str", "repeat": "", "crontab": "", "once": false, "onceDelay": 0.1, "x": 110, "y": 120, "wires": [ [ "4608374e.2fd4d8" ] ] }, { "id": "4608374e.2fd4d8", "type": "bigtimer", "z": "3c07a459.ce903c", "outtopic": "", "outpayload1": ".3", "outpayload2": ".1", "name": "Normalize Volume", "comment": "Set volume to 30% from 9:00am to 9:00pm, otherwise set to 10%", "lat": "44.50", "lon": "-88.06", "starttime": "540", "endtime": "1260", "startoff": "0", "endoff": 0, "startoff2": "", "endoff2": "", "offs": 0, "outtext1": "", "outtext2": "", "timeout": "240", "sun": true, "mon": true, "tue": true, "wed": true, "thu": true, "fri": true, "sat": true, "jan": true, "feb": true, "mar": true, "apr": true, "may": true, "jun": true, "jul": true, "aug": true, "sep": true, "oct": true, "nov": true, "dec": true, "day1": 0, "month1": 0, "day2": 0, "month2": 0, "day3": 0, "month3": 0, "day4": 0, "month4": 0, "day5": 0, "month5": 0, "day6": 0, "month6": 0, "day7": "", "month7": "", "day8": "", "month8": "", "day9": "", "month9": "", "day10": "", "month10": "", "day11": "", "month11": "", "day12": "", "month12": "", "d1": 0, "w1": 0, "d2": 0, "w2": 0, "d3": 0, "w3": 0, "d4": 0, "w4": 0, "d5": 0, "w5": 0, "d6": 0, "w6": 0, "xday1": "0", "xmonth1": "0", "xday2": "0", "xmonth2": "0", "xday3": 0, "xmonth3": 0, "xday4": 0, "xmonth4": 0, "xday5": 0, "xmonth5": 0, "xday6": 0, "xmonth6": 0, "xd1": 0, "xw1": 0, "xd2": 0, "xw2": 0, "xd3": 0, "xw3": 0, "xd4": 0, "xw4": 0, "xd5": 0, "xw5": 0, "xd6": 0, "xw6": 0, "suspend": false, "random": false, "repeat": false, "atstart": false, "odd": false, "even": false, "x": 340, "y": 100, "wires": [ [ "e949c86f.c9ca9" ], [], [] ] }, { "id": "418865ec.975eec", "type": "inject", "z": "3c07a459.ce903c", "name": "", "topic": "", "payload": "auto", "payloadType": "str", "repeat": "", "crontab": "", "once": false, "onceDelay": 0.1, "x": 110, "y": 160, "wires": [ [ "4608374e.2fd4d8" ] ] }, { "id": "e314dd72.39afe", "type": "inject", "z": "3c07a459.ce903c", "name": "", "topic": "", "payload": "manual", "payloadType": "str", "repeat": "", "crontab": "", "once": false, "onceDelay": 0.1, "x": 110, "y": 200, "wires": [ [ "4608374e.2fd4d8" ] ] }, { "id": "bde48ad8.814ac", "type": "rbe", "z": "3c07a459.ce903c", "name": "Only allow changed values", "func": "rbe", "gap": "", "start": "", "inout": "out", "property": "payload", "x": 480, "y": 180, "wires": [ [ "4173c48c.381f84" ] ] }, { "id": "4173c48c.381f84", "type": "Subflow:76be143e.dbb0e4", "z": "3c07a459.ce903c", "name": "", "env": [], "x": 790, "y": 180, "wires": [ [] ] }, { "id": "d45759b3.b76fe8", "type": "comment", "z": "3c07a459.ce903c", "name": "Set volume to 30% from 9:00am to 9:00pm, otherwise set to 10%", "info": "", "x": 270, "y": 40, "wires": [] }, { "id": "61956bd4.93df44", "type": "server", "z": "", "name": "Home Assistant", "legacy": false, "addon": true, "rejectUnauthorizedCerts": true, "ha_boolean": "y|yes|true|on|home|open", "connectionDelay": true } ]
NodeRed VS Home Assistant Automations
I personally prefer writing all my automations in NodeRed as it helps me visually see the flow of the automation. I’d love to hear what you like and if you’d like to see automations written using the built in Home Assistant Automations.
以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网
猜你喜欢:本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
C算法(第二卷:图算法)(第3版)
塞德威克(Sedgewick Robert) / 周良忠 / 第1版 (2004年1月1日) / 2004-4 / 38.0
《C算法(第2卷)(图算法)(第3版)(中文版)》所讨论的图算法,都是实际中解决图问题的最重要的已知方法。《C算法(第2卷)(图算法)(第3版)(中文版)》的主要宗旨是让越来越多需要了解这些算法的人的能够掌握这些方法及基本原理。书中根据基本原理从基本住处开始循序渐进地讲解,然后再介绍一些经典方法,最后介绍仍在进行研究和发展的现代技术。精心挑选的实例、详尽的图示以及完整的实现代码与正文中的算法和应用......一起来看看 《C算法(第二卷:图算法)(第3版)》 这本书的介绍吧!